diff options
129 files changed, 4856 insertions, 2673 deletions
diff --git a/apct-tests/perftests/core/jni/SystemPerfTest.cpp b/apct-tests/perftests/core/jni/SystemPerfTest.cpp index eb5540844808..f102e3ec0a71 100644 --- a/apct-tests/perftests/core/jni/SystemPerfTest.cpp +++ b/apct-tests/perftests/core/jni/SystemPerfTest.cpp @@ -73,7 +73,7 @@ jint JNI_OnLoad(JavaVM* jvm, void*) { return JNI_ERR; } - if (registerNativeMethods(env, "java/lang/perftests/SystemPerfTest", + if (registerNativeMethods(env, "android/perftests/SystemPerfTest", sMethods, NELEM(sMethods)) == -1) { return JNI_ERR; } diff --git a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java index afc9d0cf10e1..95a714486062 100644 --- a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java +++ b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package java.lang.perftests; +package android.perftests; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; diff --git a/api/current.txt b/api/current.txt index 445c576cf091..c7074cca6e0e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16,6 +16,7 @@ package android { field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; + field public static final java.lang.String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -4143,6 +4144,7 @@ package android.app { field public static final int MODE_ERRORED = 2; // 0x2 field public static final int MODE_IGNORED = 1; // 0x1 field public static final java.lang.String OPSTR_ADD_VOICEMAIL = "android:add_voicemail"; + field public static final java.lang.String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls"; field public static final java.lang.String OPSTR_BODY_SENSORS = "android:body_sensors"; field public static final java.lang.String OPSTR_CALL_PHONE = "android:call_phone"; field public static final java.lang.String OPSTR_CAMERA = "android:camera"; @@ -8674,6 +8676,7 @@ package android.content { field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; + field public static final java.lang.String IPSEC_SERVICE = "ipsec"; field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler"; field public static final java.lang.String KEYGUARD_SERVICE = "keyguard"; field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps"; @@ -10166,9 +10169,11 @@ package android.content.pm { method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback); method public void updateSessionAppIcon(int, android.graphics.Bitmap); method public void updateSessionAppLabel(int, java.lang.CharSequence); + field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION"; field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS"; field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; @@ -10211,6 +10216,7 @@ package android.content.pm { method public android.graphics.Bitmap getAppIcon(); method public java.lang.CharSequence getAppLabel(); method public java.lang.String getAppPackageName(); + method public int getInstallReason(); method public java.lang.String getInstallerPackageName(); method public float getProgress(); method public int getSessionId(); @@ -10600,6 +10606,7 @@ package android.content.pm { field public android.content.pm.ActivityInfo activityInfo; field public android.content.IntentFilter filter; field public int icon; + field public boolean instantAppAvailable; field public boolean isDefault; field public int labelRes; field public int match; @@ -24132,17 +24139,79 @@ package android.media.tv { method public static final android.net.Uri buildProgramsUriForChannel(long, long, long); method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long); method public static final android.net.Uri buildRecordedProgramUri(long); + method public static final android.net.Uri buildWatchNextProgramUri(long); method public static final boolean isChannelUri(android.net.Uri); method public static final boolean isChannelUriForPassthroughInput(android.net.Uri); method public static final boolean isChannelUriForTunerInput(android.net.Uri); method public static final boolean isProgramUri(android.net.Uri); + field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED"; + field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID"; + field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; + } + + public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { + field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + 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_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_TYPE = "type"; + field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; + field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; + field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; + field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; + field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; + field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; + field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; + field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; + field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; + field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; } public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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"; @@ -24175,6 +24244,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri"; field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_DESCRIPTION = "description"; field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name"; field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number"; @@ -24247,69 +24317,17 @@ 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.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; - 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_DURATION_MILLIS = "duration_millis"; - 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_TYPE = "type"; - field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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 java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; } public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; @@ -24345,6 +24363,7 @@ package android.media.tv { public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; @@ -24357,6 +24376,19 @@ package android.media.tv { field public static final android.net.Uri CONTENT_URI; } + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + ctor public TvContract.WatchNextPrograms(); + 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_WATCH_NEXT_TYPE = "watch_next_type"; + field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; + field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; + field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + } + public final class TvInputInfo implements android.os.Parcelable { method public boolean canRecord(); method public android.content.Intent createSettingsIntent(); @@ -24406,15 +24438,10 @@ package android.media.tv { method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback); method public void updateTvInputInfo(android.media.tv.TvInputInfo); field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED"; - field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED"; - field public static final java.lang.String ACTION_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"; field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS"; field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES"; - field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; - field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; - field public static final java.lang.String EXTRA_PROGRAM_ID = "android.media.tv.extra.PROGRAM_ID"; field public static final int INPUT_STATE_CONNECTED = 0; // 0x0 field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1 field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2 @@ -24986,6 +25013,68 @@ package android.net { field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; } + public final class IpSecAlgorithm implements android.os.Parcelable { + ctor public IpSecAlgorithm(java.lang.String, byte[]); + ctor public IpSecAlgorithm(java.lang.String, byte[], int); + method public int describeContents(); + method public byte[] getKey(); + method public java.lang.String getName(); + method public int getTruncationLengthBits(); + method public void writeToParcel(android.os.Parcel, int); + field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)"; + field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)"; + field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; + } + + 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 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 android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; + field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 + } + + public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { + } + + public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { + method public void close(); + method public int getSpi(); + } + + public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException { + method public int getSpi(); + } + + public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { + method public void close(); + method public int getPort(); + method public java.io.FileDescriptor getSocket(); + } + + public final class IpSecTransform implements java.lang.AutoCloseable { + method public void close(); + field public static final int DIRECTION_IN = 0; // 0x0 + field public static final int DIRECTION_OUT = 1; // 0x1 + } + + 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.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); + } + public class LinkAddress implements android.os.Parcelable { method public int describeContents(); method public java.net.InetAddress getAddress(); @@ -30036,7 +30125,7 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_BATTERY_STATUS = 6; // 0x6 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 @@ -38155,9 +38244,6 @@ package android.telecom { field public static final int RTT_MODE_VCO = 3; // 0x3 } - public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation { - } - public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -38698,6 +38784,8 @@ package android.telecom { } public class TelecomManager { + method public void acceptRingingCall(); + method public void acceptRingingCall(int); method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle); method public void cancelMissedCallsNotification(); method public android.content.Intent createManageBlockedNumbersIntent(); diff --git a/api/system-current.txt b/api/system-current.txt index e00507ee884c..a46735122ef5 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -25,6 +25,7 @@ package android { field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; + field public static final java.lang.String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"; field public static final java.lang.String BACKUP = "android.permission.BACKUP"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; @@ -4286,6 +4287,7 @@ package android.app { field public static final int MODE_IGNORED = 1; // 0x1 field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; field public static final java.lang.String OPSTR_ADD_VOICEMAIL = "android:add_voicemail"; + field public static final java.lang.String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls"; field public static final java.lang.String OPSTR_BODY_SENSORS = "android:body_sensors"; field public static final java.lang.String OPSTR_CALL_PHONE = "android:call_phone"; field public static final java.lang.String OPSTR_CAMERA = "android:camera"; @@ -9137,6 +9139,7 @@ package android.content { field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; + field public static final java.lang.String IPSEC_SERVICE = "ipsec"; field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler"; field public static final java.lang.String KEYGUARD_SERVICE = "keyguard"; field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps"; @@ -10703,9 +10706,11 @@ package android.content.pm { method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback); method public void updateSessionAppIcon(int, android.graphics.Bitmap); method public void updateSessionAppLabel(int, java.lang.CharSequence); + field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION"; field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS"; field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; @@ -10748,6 +10753,7 @@ package android.content.pm { method public android.graphics.Bitmap getAppIcon(); method public java.lang.CharSequence getAppLabel(); method public java.lang.String getAppPackageName(); + method public int getInstallReason(); method public java.lang.String getInstallerPackageName(); method public float getProgress(); method public int getSessionId(); @@ -11222,6 +11228,7 @@ package android.content.pm { field public android.content.pm.ActivityInfo activityInfo; field public android.content.IntentFilter filter; field public int icon; + field public boolean instantAppAvailable; field public boolean isDefault; field public int labelRes; field public int match; @@ -25962,23 +25969,86 @@ package android.media.tv { method public static final android.net.Uri buildProgramsUriForChannel(long, long, long); method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long); method public static final android.net.Uri buildRecordedProgramUri(long); + method public static final android.net.Uri buildWatchNextProgramUri(long); method public static final boolean isChannelUri(android.net.Uri); method public static final boolean isChannelUriForPassthroughInput(android.net.Uri); method public static final boolean isChannelUriForTunerInput(android.net.Uri); method public static final boolean isProgramUri(android.net.Uri); + field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED"; + field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; field public static final java.lang.String EXTRA_COLUMN_NAME = "android.media.tv.extra.COLUMN_NAME"; field public static final java.lang.String EXTRA_DATA_TYPE = "android.media.tv.extra.DATA_TYPE"; field public static final java.lang.String EXTRA_DEFAULT_VALUE = "android.media.tv.extra.DEFAULT_VALUE"; field public static final java.lang.String EXTRA_EXISTING_COLUMN_NAMES = "android.media.tv.extra.EXISTING_COLUMN_NAMES"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID"; + field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; field public static final java.lang.String METHOD_ADD_COLUMN = "add_column"; field public static final java.lang.String METHOD_GET_COLUMNS = "get_columns"; } + public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { + field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + 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_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; + field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; + field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; + field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; + field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; + field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; + field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; + field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; + field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; + field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + } + public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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"; @@ -26087,70 +26157,17 @@ 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.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; - 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_DURATION_MILLIS = "duration_millis"; - field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; - field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; - field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; - field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; - field public static final java.lang.String COLUMN_LIVE = "live"; - field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; - field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; - field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; - field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; - field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; - field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; - field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; - field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; - field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; - field public static final java.lang.String COLUMN_TRANSIENT = "transient"; - field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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 java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; } public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; @@ -26186,6 +26203,7 @@ package android.media.tv { public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; @@ -26198,6 +26216,19 @@ package android.media.tv { field public static final android.net.Uri CONTENT_URI; } + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + ctor public TvContract.WatchNextPrograms(); + 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_WATCH_NEXT_TYPE = "watch_next_type"; + field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; + field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; + field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + } + public static final class TvContract.WatchedPrograms implements android.media.tv.TvContract.BaseTvColumns { field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_DESCRIPTION = "description"; @@ -26327,15 +26358,10 @@ package android.media.tv { method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback); method public void updateTvInputInfo(android.media.tv.TvInputInfo); field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED"; - field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED"; - field public static final java.lang.String ACTION_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"; field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS"; field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES"; - field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; - field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; - field public static final java.lang.String EXTRA_PROGRAM_ID = "android.media.tv.extra.PROGRAM_ID"; field public static final int INPUT_STATE_CONNECTED = 0; // 0x0 field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1 field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2 @@ -27011,6 +27037,73 @@ package android.net { field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; } + public final class IpSecAlgorithm implements android.os.Parcelable { + ctor public IpSecAlgorithm(java.lang.String, byte[]); + ctor public IpSecAlgorithm(java.lang.String, byte[], int); + method public int describeContents(); + method public byte[] getKey(); + method public java.lang.String getName(); + method public int getTruncationLengthBits(); + method public void writeToParcel(android.os.Parcel, int); + field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)"; + field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)"; + field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; + } + + 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; + field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 + } + + public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { + } + + public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { + method public void close(); + method public int getSpi(); + } + + public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException { + method public int getSpi(); + } + + public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { + method public void close(); + method public int getPort(); + method public java.io.FileDescriptor getSocket(); + } + + public final class IpSecTransform implements java.lang.AutoCloseable { + method public void close(); + field public static final int DIRECTION_IN = 0; // 0x0 + field public static final int DIRECTION_OUT = 1; // 0x1 + } + + 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); + } + public class LinkAddress implements android.os.Parcelable { method public int describeContents(); method public java.net.InetAddress getAddress(); @@ -32634,7 +32727,7 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_BATTERY_STATUS = 6; // 0x6 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 @@ -41206,9 +41299,6 @@ package android.telecom { field public static final int RTT_MODE_VCO = 3; // 0x3 } - public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation { - } - public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -42514,7 +42604,9 @@ package android.telephony { method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); method public void sendMultimediaMessage(android.content.Context, android.net.Uri, java.lang.String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); + method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>); method public void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent); + method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent); field public static final java.lang.String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA"; field public static final java.lang.String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS"; field public static final java.lang.String MMS_CONFIG_ALIAS_ENABLED = "aliasEnabled"; diff --git a/api/test-current.txt b/api/test-current.txt index 500a0569bfd2..1a12b4c088eb 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -16,6 +16,7 @@ package android { field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; + field public static final java.lang.String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -4153,6 +4154,7 @@ package android.app { field public static final int MODE_ERRORED = 2; // 0x2 field public static final int MODE_IGNORED = 1; // 0x1 field public static final java.lang.String OPSTR_ADD_VOICEMAIL = "android:add_voicemail"; + field public static final java.lang.String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls"; field public static final java.lang.String OPSTR_BODY_SENSORS = "android:body_sensors"; field public static final java.lang.String OPSTR_CALL_PHONE = "android:call_phone"; field public static final java.lang.String OPSTR_CAMERA = "android:camera"; @@ -8702,6 +8704,7 @@ package android.content { field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; + field public static final java.lang.String IPSEC_SERVICE = "ipsec"; field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler"; field public static final java.lang.String KEYGUARD_SERVICE = "keyguard"; field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps"; @@ -10198,9 +10201,11 @@ package android.content.pm { method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback); method public void updateSessionAppIcon(int, android.graphics.Bitmap); method public void updateSessionAppLabel(int, java.lang.CharSequence); + field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION"; field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS"; field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; @@ -10243,6 +10248,7 @@ package android.content.pm { method public android.graphics.Bitmap getAppIcon(); method public java.lang.CharSequence getAppLabel(); method public java.lang.String getAppPackageName(); + method public int getInstallReason(); method public java.lang.String getInstallerPackageName(); method public float getProgress(); method public int getSessionId(); @@ -10636,6 +10642,7 @@ package android.content.pm { field public android.content.pm.ActivityInfo activityInfo; field public android.content.IntentFilter filter; field public int icon; + field public boolean instantAppAvailable; field public boolean isDefault; field public int labelRes; field public int match; @@ -24228,17 +24235,79 @@ package android.media.tv { method public static final android.net.Uri buildProgramsUriForChannel(long, long, long); method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long); method public static final android.net.Uri buildRecordedProgramUri(long); + method public static final android.net.Uri buildWatchNextProgramUri(long); method public static final boolean isChannelUri(android.net.Uri); method public static final boolean isChannelUriForPassthroughInput(android.net.Uri); method public static final boolean isChannelUriForTunerInput(android.net.Uri); method public static final boolean isProgramUri(android.net.Uri); + field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT"; + field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED"; + field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID"; + field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; + } + + public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { + field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + 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_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_TYPE = "type"; + field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; + field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; + field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; + field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; + field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; + field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; + field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; + field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; + field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; + field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; } public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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"; @@ -24271,6 +24340,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri"; field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_DESCRIPTION = "description"; field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name"; field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number"; @@ -24343,69 +24413,17 @@ 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.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; - 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_DURATION_MILLIS = "duration_millis"; - 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_TYPE = "type"; - field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; 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 java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; } public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; @@ -24441,6 +24459,7 @@ package android.media.tv { public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; @@ -24453,6 +24472,19 @@ package android.media.tv { field public static final android.net.Uri CONTENT_URI; } + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + ctor public TvContract.WatchNextPrograms(); + 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_WATCH_NEXT_TYPE = "watch_next_type"; + field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; + field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; + field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; + field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + } + public final class TvInputInfo implements android.os.Parcelable { method public boolean canRecord(); method public android.content.Intent createSettingsIntent(); @@ -24502,15 +24534,10 @@ package android.media.tv { method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback); method public void updateTvInputInfo(android.media.tv.TvInputInfo); field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED"; - field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED"; - field public static final java.lang.String ACTION_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PROGRAM_BROWSABLE_DISABLED"; field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"; field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS"; field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES"; - field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; - field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; - field public static final java.lang.String EXTRA_PROGRAM_ID = "android.media.tv.extra.PROGRAM_ID"; field public static final int INPUT_STATE_CONNECTED = 0; // 0x0 field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1 field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2 @@ -25082,6 +25109,68 @@ package android.net { field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; } + public final class IpSecAlgorithm implements android.os.Parcelable { + ctor public IpSecAlgorithm(java.lang.String, byte[]); + ctor public IpSecAlgorithm(java.lang.String, byte[], int); + method public int describeContents(); + method public byte[] getKey(); + method public java.lang.String getName(); + method public int getTruncationLengthBits(); + method public void writeToParcel(android.os.Parcel, int); + field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)"; + field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)"; + field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)"; + field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; + } + + 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 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 android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; + field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 + } + + public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { + } + + public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { + method public void close(); + method public int getSpi(); + } + + public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException { + method public int getSpi(); + } + + public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { + method public void close(); + method public int getPort(); + method public java.io.FileDescriptor getSocket(); + } + + public final class IpSecTransform implements java.lang.AutoCloseable { + method public void close(); + field public static final int DIRECTION_IN = 0; // 0x0 + field public static final int DIRECTION_OUT = 1; // 0x1 + } + + 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.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); + } + public class LinkAddress implements android.os.Parcelable { method public int describeContents(); method public java.net.InetAddress getAddress(); @@ -30132,7 +30221,7 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_BATTERY_STATUS = 6; // 0x6 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 @@ -38339,9 +38428,6 @@ package android.telecom { field public static final int RTT_MODE_VCO = 3; // 0x3 } - public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation { - } - public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -38882,6 +38968,8 @@ package android.telecom { } public class TelecomManager { + method public void acceptRingingCall(); + method public void acceptRingingCall(int); method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle); method public void cancelMissedCallsNotification(); method public android.content.Intent createManageBlockedNumbersIntent(); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 1aef3639e449..91520f1a7d8e 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -547,6 +547,12 @@ public final class Pm { throw new IllegalArgumentException("Missing inherit package name"); } break; + case "--pkg": + sessionParams.appPackageName = nextOptionData(); + if (sessionParams.appPackageName == null) { + throw new IllegalArgumentException("Missing package name"); + } + break; case "-S": final long sizeBytes = Long.parseLong(nextOptionData()); if (sizeBytes <= 0) { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6dd31a852172..0f2ce3c09f00 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -249,8 +249,10 @@ public class AppOpsManager { public static final int OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67; /** @hide Instant app start foreground service. */ public static final int OP_INSTANT_APP_START_FOREGROUND = 68; + /** @hide Answer incoming phone calls */ + public static final int OP_ANSWER_PHONE_CALLS = 69; /** @hide */ - public static final int _NUM_OP = 69; + public static final int _NUM_OP = 70; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -356,6 +358,9 @@ public class AppOpsManager { /** @hide */ public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; + /** Answer incoming phone calls */ + public static final String OPSTR_ANSWER_PHONE_CALLS + = "android:answer_phone_calls"; private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = { // RUNTIME PERMISSIONS @@ -388,6 +393,7 @@ public class AppOpsManager { OP_ADD_VOICEMAIL, OP_USE_SIP, OP_PROCESS_OUTGOING_CALLS, + OP_ANSWER_PHONE_CALLS, // Microphone OP_RECORD_AUDIO, // Camera @@ -480,6 +486,7 @@ public class AppOpsManager { OP_REQUEST_INSTALL_PACKAGES, OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, OP_INSTANT_APP_START_FOREGROUND, + OP_ANSWER_PHONE_CALLS }; /** @@ -556,6 +563,7 @@ public class AppOpsManager { null, // OP_REQUEST_INSTALL_PACKAGES null, OPSTR_INSTANT_APP_START_FOREGROUND, + OPSTR_ANSWER_PHONE_CALLS, }; /** @@ -632,6 +640,7 @@ public class AppOpsManager { "REQUEST_INSTALL_PACKAGES", "OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE", "INSTANT_APP_START_FOREGROUND", + "ANSWER_PHONE_CALLS", }; /** @@ -708,6 +717,7 @@ public class AppOpsManager { Manifest.permission.REQUEST_INSTALL_PACKAGES, null, // no permission for entering picture-in-picture on hide Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, + Manifest.permission.ANSWER_PHONE_CALLS, }; /** @@ -785,6 +795,7 @@ public class AppOpsManager { null, // REQUEST_INSTALL_PACKAGES null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE null, // INSTANT_APP_START_FOREGROUND + null, // ANSWER_PHONE_CALLS }; /** @@ -861,6 +872,7 @@ public class AppOpsManager { false, // REQUEST_INSTALL_PACKAGES false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE false, // INSTANT_APP_START_FOREGROUND + false, // ANSWER_PHONE_CALLS }; /** @@ -936,6 +948,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // OP_REQUEST_INSTALL_PACKAGES AppOpsManager.MODE_ALLOWED, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND + AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS }; /** @@ -1015,6 +1028,7 @@ public class AppOpsManager { false, // OP_REQUEST_INSTALL_PACKAGES false, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE false, + false, // ANSWER_PHONE_CALLS }; /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 5205959361a5..d37e2099c1d7 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -447,6 +447,19 @@ public class NotificationManager } /** + * @hide + */ + public void createNotificationChannelsForPackage(String pkg, + @NonNull List<NotificationChannel> channels) { + INotificationManager service = getService(); + try { + service.createNotificationChannels(pkg, new ParceledListSlice(channels)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the notification channel settings for a given channel id. */ public NotificationChannel getNotificationChannel(String channelId) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index aff00c3e84b6..de503c0f5499 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2711,6 +2711,7 @@ public abstract class Context { VIBRATOR_SERVICE, //@hide: STATUS_BAR_SERVICE, CONNECTIVITY_SERVICE, + IPSEC_SERVICE, //@hide: UPDATE_LOCK_SERVICE, //@hide: NETWORKMANAGEMENT_SERVICE, NETWORK_STATS_SERVICE, @@ -2814,6 +2815,9 @@ public abstract class Context { * <dt> {@link #CONNECTIVITY_SERVICE} ("connection") * <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for * handling management of network connections. + * <dt> {@link #IPSEC_SERVICE} ("ipsec") + * <dd> A {@link android.net.IpSecManager IpSecManager} for managing IPSec on + * sockets and networks. * <dt> {@link #WIFI_SERVICE} ("wifi") * <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi * connectivity. On releases before NYC, it should only be obtained from an application @@ -3154,6 +3158,15 @@ public abstract class Context { public static final String CONNECTIVITY_SERVICE = "connectivity"; /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.net.IpSecManager} for encrypting Sockets or Networks with + * IPSec. + * + * @see #getSystemService + */ + public static final String IPSEC_SERVICE = "ipsec"; + + /** * Use with {@link #getSystemService} to retrieve a {@link * android.os.IUpdateLock} for managing runtime sequences that * must not be interrupted by headless OTA application or similar. diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java new file mode 100644 index 000000000000..c4e4e06be749 --- /dev/null +++ b/core/java/android/content/pm/BaseParceledListSlice.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2011 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.content.pm; + +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Transfer a large list of Parcelable objects across an IPC. Splits into + * multiple transactions if needed. + * + * Caveat: for efficiency and security, all elements must be the same concrete type. + * In order to avoid writing the class name of each object, we must ensure that + * each object is the same type, or else unparceling then reparceling the data may yield + * a different result if the class name encoded in the Parcelable is a Base type. + * See b/17671747. + * + * @hide + */ +abstract class BaseParceledListSlice<T> implements Parcelable { + private static String TAG = "ParceledListSlice"; + private static boolean DEBUG = false; + + /* + * TODO get this number from somewhere else. For now set it to a quarter of + * the 1MB limit. + */ + private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE; + + private final List<T> mList; + + public BaseParceledListSlice(List<T> list) { + mList = list; + } + + @SuppressWarnings("unchecked") + BaseParceledListSlice(Parcel p, ClassLoader loader) { + final int N = p.readInt(); + mList = new ArrayList<T>(N); + if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); + if (N <= 0) { + return; + } + + Parcelable.Creator<?> creator = readParcelableCreator(p, loader); + Class<?> listElementClass = null; + + int i = 0; + while (i < N) { + if (p.readInt() == 0) { + break; + } + + final T parcelable = readCreator(creator, p, loader); + if (listElementClass == null) { + listElementClass = parcelable.getClass(); + } else { + verifySameType(listElementClass, parcelable.getClass()); + } + + mList.add(parcelable); + + if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); + i++; + } + if (i >= N) { + return; + } + final IBinder retriever = p.readStrongBinder(); + while (i < N) { + if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInt(i); + try { + retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); + return; + } + while (i < N && reply.readInt() != 0) { + final T parcelable = reply.readCreator(creator, loader); + verifySameType(listElementClass, parcelable.getClass()); + + mList.add(parcelable); + + if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); + i++; + } + reply.recycle(); + data.recycle(); + } + } + + private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) { + if (creator instanceof Parcelable.ClassLoaderCreator<?>) { + Parcelable.ClassLoaderCreator<?> classLoaderCreator = + (Parcelable.ClassLoaderCreator<?>) creator; + return (T) classLoaderCreator.createFromParcel(p, loader); + } + return (T) creator.createFromParcel(p); + } + + private static void verifySameType(final Class<?> expected, final Class<?> actual) { + if (!actual.equals(expected)) { + throw new IllegalArgumentException("Can't unparcel type " + + actual.getName() + " in list of type " + + expected.getName()); + } + } + + public List<T> getList() { + return mList; + } + + /** + * 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. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + final int N = mList.size(); + final int callFlags = flags; + dest.writeInt(N); + if (DEBUG) Log.d(TAG, "Writing " + N + " items"); + if (N > 0) { + final Class<?> listElementClass = mList.get(0).getClass(); + writeParcelableCreator(mList.get(0), dest); + int i = 0; + while (i < N && dest.dataSize() < MAX_IPC_SIZE) { + dest.writeInt(1); + + final T parcelable = mList.get(i); + verifySameType(listElementClass, parcelable.getClass()); + writeElement(parcelable, dest, callFlags); + + if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); + i++; + } + if (i < N) { + dest.writeInt(0); + Binder retriever = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + int i = data.readInt(); + if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); + while (i < N && reply.dataSize() < MAX_IPC_SIZE) { + reply.writeInt(1); + + final T parcelable = mList.get(i); + verifySameType(listElementClass, parcelable.getClass()); + writeElement(parcelable, reply, callFlags); + + if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); + i++; + } + if (i < N) { + if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); + reply.writeInt(0); + } + return true; + } + }; + if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); + dest.writeStrongBinder(retriever); + } + } + } + + protected abstract void writeElement(T parcelable, Parcel reply, int callFlags); + + protected abstract void writeParcelableCreator(T parcelable, Parcel dest); + + protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader); +} diff --git a/core/java/android/content/pm/InstantAppInfo.java b/core/java/android/content/pm/InstantAppInfo.java index 898ee1101c07..67afc928fd78 100644 --- a/core/java/android/content/pm/InstantAppInfo.java +++ b/core/java/android/content/pm/InstantAppInfo.java @@ -75,7 +75,7 @@ public final class InstantAppInfo implements Parcelable { } /** - * @return The pakcage name. + * @return The package name. */ public @NonNull String getPackageName() { if (mApplicationInfo != null) { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 278a6d09d9fe..9d04cc9bff5e 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -95,6 +95,18 @@ public class PackageInstaller { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; + /** + * Broadcast Action: Explicit broadcast sent to the last known default launcher when a session + * for a new install is committed. For managed profile, this is sent to the default launcher + * of the primary profile. + * <p> + * The associated session is defined in {@link #EXTRA_SESSION} and the user for which this + * session was created in {@link Intent#EXTRA_USER}. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SESSION_COMMITTED = + "android.content.pm.action.SESSION_COMMITTED"; + /** {@hide} */ public static final String ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS"; @@ -107,6 +119,13 @@ public class PackageInstaller { public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; /** + * {@link SessionInfo} that an operation is working with. + * + * @see Intent#getParcelableExtra(String) + */ + public static final String EXTRA_SESSION = "android.content.pm.extra.SESSION"; + + /** * Package name that an operation is working with. * * @see Intent#getStringExtra(String) @@ -1184,6 +1203,8 @@ public class PackageInstaller { /** {@hide} */ public int mode; /** {@hide} */ + public int installReason; + /** {@hide} */ public long sizeBytes; /** {@hide} */ public String appPackageName; @@ -1206,6 +1227,7 @@ public class PackageInstaller { active = source.readInt() != 0; mode = source.readInt(); + installReason = source.readInt(); sizeBytes = source.readLong(); appPackageName = source.readString(); appIcon = source.readParcelable(null); @@ -1256,6 +1278,15 @@ public class PackageInstaller { return active; } + /** + * Return the reason for installing this package. + * + * @see PackageManager#INSTALL_REASON_UNKNOWN + */ + public int getInstallReason() { + return installReason; + } + /** {@hide} */ @Deprecated public boolean isOpen() { @@ -1324,6 +1355,7 @@ public class PackageInstaller { dest.writeInt(active ? 1 : 0); dest.writeInt(mode); + dest.writeInt(installReason); dest.writeLong(sizeBytes); dest.writeString(appPackageName); dest.writeParcelable(appIcon, flags); diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java index 945858e6d3c6..d12e8846aabb 100644 --- a/core/java/android/content/pm/ParceledListSlice.java +++ b/core/java/android/content/pm/ParceledListSlice.java @@ -16,14 +16,9 @@ package android.content.pm; -import android.os.Binder; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -31,171 +26,46 @@ import java.util.List; * Transfer a large list of Parcelable objects across an IPC. Splits into * multiple transactions if needed. * - * Caveat: for efficiency and security, all elements must be the same concrete type. - * In order to avoid writing the class name of each object, we must ensure that - * each object is the same type, or else unparceling then reparceling the data may yield - * a different result if the class name encoded in the Parcelable is a Base type. - * See b/17671747. + * @see BaseParceledListSlice * * @hide */ -public class ParceledListSlice<T extends Parcelable> implements Parcelable { - private static String TAG = "ParceledListSlice"; - private static boolean DEBUG = false; - - /* - * TODO get this number from somewhere else. For now set it to a quarter of - * the 1MB limit. - */ - private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE; - - private final List<T> mList; - - public static <T extends Parcelable> ParceledListSlice<T> emptyList() { - return new ParceledListSlice<T>(Collections.<T> emptyList()); - } - +public class ParceledListSlice<T extends Parcelable> extends BaseParceledListSlice<T> { public ParceledListSlice(List<T> list) { - mList = list; - } - - @SuppressWarnings("unchecked") - private ParceledListSlice(Parcel p, ClassLoader loader) { - final int N = p.readInt(); - mList = new ArrayList<T>(N); - if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); - if (N <= 0) { - return; - } - - Parcelable.Creator<?> creator = p.readParcelableCreator(loader); - Class<?> listElementClass = null; - - int i = 0; - while (i < N) { - if (p.readInt() == 0) { - break; - } - - final T parcelable = p.readCreator(creator, loader); - if (listElementClass == null) { - listElementClass = parcelable.getClass(); - } else { - verifySameType(listElementClass, parcelable.getClass()); - } - - mList.add(parcelable); - - if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); - i++; - } - if (i >= N) { - return; - } - final IBinder retriever = p.readStrongBinder(); - while (i < N) { - if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInt(i); - try { - retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); - return; - } - while (i < N && reply.readInt() != 0) { - final T parcelable = reply.readCreator(creator, loader); - verifySameType(listElementClass, parcelable.getClass()); - - mList.add(parcelable); - - if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); - i++; - } - reply.recycle(); - data.recycle(); - } + super(list); } - private static void verifySameType(final Class<?> expected, final Class<?> actual) { - if (!actual.equals(expected)) { - throw new IllegalArgumentException("Can't unparcel type " - + actual.getName() + " in list of type " - + expected.getName()); - } + private ParceledListSlice(Parcel in, ClassLoader loader) { + super(in, loader); } - public List<T> getList() { - return mList; + public static <T extends Parcelable> ParceledListSlice<T> emptyList() { + return new ParceledListSlice<T>(Collections.<T> emptyList()); } @Override public int describeContents() { int contents = 0; - for (int i=0; i<mList.size(); i++) { - contents |= mList.get(i).describeContents(); + final List<T> list = getList(); + for (int i=0; i<list.size(); i++) { + contents |= list.get(i).describeContents(); } return contents; } - /** - * 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. - */ @Override - public void writeToParcel(Parcel dest, int flags) { - final int N = mList.size(); - final int callFlags = flags; - dest.writeInt(N); - if (DEBUG) Log.d(TAG, "Writing " + N + " items"); - if (N > 0) { - final Class<?> listElementClass = mList.get(0).getClass(); - dest.writeParcelableCreator(mList.get(0)); - int i = 0; - while (i < N && dest.dataSize() < MAX_IPC_SIZE) { - dest.writeInt(1); - - final T parcelable = mList.get(i); - verifySameType(listElementClass, parcelable.getClass()); - parcelable.writeToParcel(dest, callFlags); - - if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); - i++; - } - if (i < N) { - dest.writeInt(0); - Binder retriever = new Binder() { - @Override - protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - if (code != FIRST_CALL_TRANSACTION) { - return super.onTransact(code, data, reply, flags); - } - int i = data.readInt(); - if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); - while (i < N && reply.dataSize() < MAX_IPC_SIZE) { - reply.writeInt(1); + protected void writeElement(T parcelable, Parcel dest, int callFlags) { + parcelable.writeToParcel(dest, callFlags); + } - final T parcelable = mList.get(i); - verifySameType(listElementClass, parcelable.getClass()); - parcelable.writeToParcel(reply, callFlags); + @Override + protected void writeParcelableCreator(T parcelable, Parcel dest) { + dest.writeParcelableCreator((Parcelable) parcelable); + } - if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); - i++; - } - if (i < N) { - if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); - reply.writeInt(0); - } - return true; - } - }; - if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); - dest.writeStrongBinder(retriever); - } - } + @Override + protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) { + return from.readParcelableCreator(loader); } @SuppressWarnings("unchecked") @@ -210,6 +80,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable { return new ParceledListSlice(in, loader); } + @Override public ParceledListSlice[] newArray(int size) { return new ParceledListSlice[size]; } diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 50f2d5371019..650b4c008dcc 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -69,6 +69,11 @@ public class ResolveInfo implements Parcelable { public AuxiliaryResolveInfo auxiliaryInfo; /** + * Whether or not an instant app is available for the resolved intent. + */ + public boolean instantAppAvailable; + + /** * The IntentFilter that was matched for this ResolveInfo. */ public IntentFilter filter; @@ -325,6 +330,7 @@ public class ResolveInfo implements Parcelable { system = orig.system; targetUserId = orig.targetUserId; handleAllWebDataURI = orig.handleAllWebDataURI; + instantAppAvailable = orig.instantAppAvailable; } public String toString() { @@ -388,6 +394,7 @@ public class ResolveInfo implements Parcelable { dest.writeInt(noResourceId ? 1 : 0); dest.writeInt(iconResourceId); dest.writeInt(handleAllWebDataURI ? 1 : 0); + dest.writeInt(instantAppAvailable ? 1 : 0); } public static final Creator<ResolveInfo> CREATOR @@ -435,6 +442,7 @@ public class ResolveInfo implements Parcelable { noResourceId = source.readInt() != 0; iconResourceId = source.readInt(); handleAllWebDataURI = source.readInt() != 0; + instantAppAvailable = source.readInt() != 0; } public static class DisplayNameComparator diff --git a/core/java/android/content/pm/StringParceledListSlice.aidl b/core/java/android/content/pm/StringParceledListSlice.aidl new file mode 100644 index 000000000000..345f3a7b5e3e --- /dev/null +++ b/core/java/android/content/pm/StringParceledListSlice.aidl @@ -0,0 +1,19 @@ +/* + * 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.content.pm; + +parcelable StringParceledListSlice; diff --git a/core/java/android/content/pm/StringParceledListSlice.java b/core/java/android/content/pm/StringParceledListSlice.java new file mode 100644 index 000000000000..95407449a018 --- /dev/null +++ b/core/java/android/content/pm/StringParceledListSlice.java @@ -0,0 +1,83 @@ +/* + * 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.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Collections; +import java.util.List; + +/** + * Transfer a large list of Parcelable objects across an IPC. Splits into + * multiple transactions if needed. + * + * @see BaseParceledListSlice + * + * @hide + */ +public class StringParceledListSlice extends BaseParceledListSlice<String> { + public StringParceledListSlice(List<String> list) { + super(list); + } + + private StringParceledListSlice(Parcel in, ClassLoader loader) { + super(in, loader); + } + + public static StringParceledListSlice emptyList() { + return new StringParceledListSlice(Collections.<String> emptyList()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + protected void writeElement(String parcelable, Parcel reply, int callFlags) { + reply.writeString(parcelable); + } + + @Override + protected void writeParcelableCreator(String parcelable, Parcel dest) { + return; + } + + @Override + protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) { + return Parcel.STRING_CREATOR; + } + + @SuppressWarnings("unchecked") + public static final Parcelable.ClassLoaderCreator<StringParceledListSlice> CREATOR = + new Parcelable.ClassLoaderCreator<StringParceledListSlice>() { + public StringParceledListSlice createFromParcel(Parcel in) { + return new StringParceledListSlice(in, null); + } + + @Override + public StringParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { + return new StringParceledListSlice(in, loader); + } + + @Override + public StringParceledListSlice[] newArray(int size) { + return new StringParceledListSlice[size]; + } + }; +} diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java new file mode 100644 index 000000000000..da5cb37961ab --- /dev/null +++ b/core/java/android/net/IpSecAlgorithm.java @@ -0,0 +1,181 @@ +/* + * 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.net; + +import android.annotation.StringDef; +import android.os.Parcel; +import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to + * RFC 4301. + */ +public final class IpSecAlgorithm implements Parcelable { + + /** + * AES-CBC Encryption/Ciphering Algorithm. + * + * <p>Valid lengths for this key are {128, 192, 256}. + */ + public static final String ALGO_CRYPT_AES_CBC = "cbc(aes)"; + + /** + * MD5 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in new + * applications and is provided for legacy compatibility with 3gpp infrastructure. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128. + */ + public static final String ALGO_AUTH_HMAC_MD5 = "hmac(md5)"; + + /** + * SHA1 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in + * new applications and is provided for legacy compatibility with 3gpp infrastructure. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160. + */ + public static final String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)"; + + /** + * SHA256 HMAC Authentication/Integrity Algorithm. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256. + */ + public static final String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)"; + + /** + * SHA384 HMAC Authentication/Integrity Algorithm. + * + * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384. + */ + public static final String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)"; + /** + * SHA512 HMAC Authentication/Integrity Algorithm + * + * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512. + */ + public static final String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)"; + + /** @hide */ + @StringDef({ + ALGO_CRYPT_AES_CBC, + ALGO_AUTH_HMAC_MD5, + ALGO_AUTH_HMAC_SHA1, + ALGO_AUTH_HMAC_SHA256, + ALGO_AUTH_HMAC_SHA512 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlgorithmName {} + + private final String mName; + private final byte[] mKey; + private final int mTruncLenBits; + + /** + * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the + * algorithm + * + * @param algorithm type for IpSec. + * @param key non-null Key padded to a multiple of 8 bits. + */ + public IpSecAlgorithm(String algorithm, byte[] key) { + this(algorithm, key, key.length * 8); + } + + /** + * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the + * algorithm + * + * @param algoName precise name of the algorithm to be used. + * @param key non-null Key padded to a multiple of 8 bits. + * @param truncLenBits the number of bits of output hash to use; only meaningful for + * Authentication. + */ + public IpSecAlgorithm(@AlgorithmName String algoName, byte[] key, int truncLenBits) { + if (!isTruncationLengthValid(algoName, truncLenBits)) { + throw new IllegalArgumentException("Unknown algorithm or invalid length"); + } + mName = algoName; + mKey = key.clone(); + mTruncLenBits = Math.min(truncLenBits, key.length * 8); + } + + /** Retrieve the algorithm name */ + public String getName() { + return mName; + } + + /** Retrieve the key for this algorithm */ + public byte[] getKey() { + return mKey.clone(); + } + + /** + * Retrieve the truncation length, in bits, for the key in this algo. By default this will be + * the length in bits of the key. + */ + public int getTruncationLengthBits() { + return mTruncLenBits; + } + + /* Parcelable Implementation */ + public int describeContents() { + return 0; + } + + /** Write to parcel */ + public void writeToParcel(Parcel out, int flags) { + out.writeString(mName); + out.writeByteArray(mKey); + out.writeInt(mTruncLenBits); + } + + /** Parcelable Creator */ + public static final Parcelable.Creator<IpSecAlgorithm> CREATOR = + new Parcelable.Creator<IpSecAlgorithm>() { + public IpSecAlgorithm createFromParcel(Parcel in) { + return new IpSecAlgorithm(in); + } + + public IpSecAlgorithm[] newArray(int size) { + return new IpSecAlgorithm[size]; + } + }; + + private IpSecAlgorithm(Parcel in) { + mName = in.readString(); + mKey = in.createByteArray(); + mTruncLenBits = in.readInt(); + } + + private static boolean isTruncationLengthValid(String algo, int truncLenBits) { + switch (algo) { + case ALGO_AUTH_HMAC_MD5: + return (truncLenBits >= 96 && truncLenBits <= 128); + case ALGO_AUTH_HMAC_SHA1: + return (truncLenBits >= 96 && truncLenBits <= 160); + case ALGO_AUTH_HMAC_SHA256: + return (truncLenBits >= 96 && truncLenBits <= 256); + case ALGO_AUTH_HMAC_SHA384: + return (truncLenBits >= 192 && truncLenBits <= 384); + case ALGO_AUTH_HMAC_SHA512: + return (truncLenBits >= 256 && truncLenBits <= 512); + default: + return false; + } + } +}; diff --git a/core/java/android/net/IpSecConfig.aidl b/core/java/android/net/IpSecConfig.aidl new file mode 100644 index 000000000000..eaefca74d3a0 --- /dev/null +++ b/core/java/android/net/IpSecConfig.aidl @@ -0,0 +1,20 @@ +/* + * 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.net; + +/** @hide */ +parcelable IpSecConfig; diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java new file mode 100644 index 000000000000..b58bf421a86a --- /dev/null +++ b/core/java/android/net/IpSecConfig.java @@ -0,0 +1,197 @@ +/* + * 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.net; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** @hide */ +public final class IpSecConfig implements Parcelable { + private static final String TAG = IpSecConfig.class.getSimpleName(); + + //MODE_TRANSPORT or MODE_TUNNEL + int mode; + + // For tunnel mode + InetAddress localAddress; + + InetAddress remoteAddress; + + // Limit selection by network interface + Network network; + + public static class Flow { + // Minimum requirements for identifying a transform + // SPI identifying the IPsec flow in packet processing + // and a remote IP address + int spi; + + // Encryption Algorithm + IpSecAlgorithm encryptionAlgo; + + // Authentication Algorithm + IpSecAlgorithm authenticationAlgo; + } + + Flow[] flow = new Flow[2]; + + // For tunnel mode IPv4 UDP Encapsulation + // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE + int encapType; + 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() { + return localAddress; + } + + public int getSpi(int direction) { + return flow[direction].spi; + } + + public InetAddress getRemoteIp() { + return remoteAddress; + } + + public IpSecAlgorithm getEncryptionAlgo(int direction) { + return flow[direction].encryptionAlgo; + } + + public IpSecAlgorithm getAuthenticationAlgo(int direction) { + return flow[direction].authenticationAlgo; + } + + Network getNetwork() { + return network; + } + + public int getEncapType() { + return encapType; + } + + public int getEncapLocalPort() { + return encapLocalPort; + } + + public int getEncapRemotePort() { + return encapRemotePort; + } + + public int getSelectorProto() { + return selectorProto; + } + + int getNattKeepaliveInterval() { + return nattKeepaliveInterval; + } + + public boolean hasProperty(int featureBits) { + return (features & featureBits) == featureBits; + } + + // Parcelable Methods + + @Override + public int describeContents() { + return 0; + } + + @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.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi); + out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags); + out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, 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 + } + + private static InetAddress readInetAddressFromParcel(Parcel in) { + String addrString = in.readString(); + if (addrString == null) { + return null; + } + try { + return InetAddress.getByName(addrString); + } catch (UnknownHostException e) { + Log.wtf(TAG, "Invalid IpAddress " + addrString); + return null; + } + } + + 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 = + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); + flow[IpSecTransform.DIRECTION_IN].authenticationAlgo = + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); + flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt(); + flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo = + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); + flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo = + (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 = + new Parcelable.Creator<IpSecConfig>() { + public IpSecConfig createFromParcel(Parcel in) { + return new IpSecConfig(in); + } + + public IpSecConfig[] newArray(int size) { + return new IpSecConfig[size]; + } + }; +} diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java new file mode 100644 index 000000000000..2c544e9b9bfe --- /dev/null +++ b/core/java/android/net/IpSecManager.java @@ -0,0 +1,379 @@ +/* + * 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.net; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.SystemApi; +import android.content.Context; +import android.os.INetworkManagementService; +import android.os.ParcelFileDescriptor; +import android.util.AndroidException; +import dalvik.system.CloseGuard; +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Socket; + +/** + * This class contains methods for managing IPsec sessions, which will perform kernel-space + * encryption and decryption of socket or Network traffic. + * + * <p>An IpSecManager may be obtained by calling {@link + * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link + * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE} + */ +public final class IpSecManager { + private static final String TAG = "IpSecManager"; + + /** + * 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 + * or reserved using reserveSecurityParameterIndex. + */ + public static final class SpiUnavailableException extends AndroidException { + private final int mSpi; + + /** + * Construct an exception indicating that a transform with the given SPI is already in use + * or otherwise unavailable. + * + * @param msg Description indicating the colliding SPI + * @param spi the SPI that could not be used due to a collision + */ + SpiUnavailableException(String msg, int spi) { + super(msg + "(spi: " + spi + ")"); + mSpi = spi; + } + + /** Retrieve the SPI that caused a collision */ + public int getSpi() { + return mSpi; + } + } + + /** + * Indicates that the requested system resource for IPsec, such as a socket or other system + * resource is unavailable. If this exception is thrown, try releasing allocated objects of the + * type requested. + */ + public static final class ResourceUnavailableException extends AndroidException { + + ResourceUnavailableException(String msg) { + super(msg); + } + } + + private final Context mContext; + private final INetworkManagementService mService; + + public static final class SecurityParameterIndex implements AutoCloseable { + private final Context mContext; + private final InetAddress mDestinationAddress; + private final CloseGuard mCloseGuard = CloseGuard.get(); + private int mSpi; + + /** 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. + */ + @Override + public void close() { + mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI + mCloseGuard.close(); + } + + @Override + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + 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; + + /** + * Reserve an SPI for traffic bound towards the specified destination 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 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 + * for this user + * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved + */ + public SecurityParameterIndex reserveSecurityParameterIndex( + InetAddress destinationAddress, int requestedSpi) + throws SpiUnavailableException, ResourceUnavailableException { + return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi); + } + + /** + * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec + * encapsulation of the traffic flowing between the socket and the remote InetAddress of that + * transform. For security reasons, attempts to send traffic to any IP address other than the + * address associated with that transform will throw an IOException. In addition, if the + * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to + * send() or receive() until the transform is removed from the socket by calling {@link + * #removeTransportModeTransform(Socket, IpSecTransform)}; + * + * @param socket a stream socket + * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. + */ + public void applyTransportModeTransform(Socket socket, IpSecTransform transform) + throws IOException { + applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform); + } + + /** + * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec + * encapsulation of the traffic flowing between the socket and the remote InetAddress of that + * transform. For security reasons, attempts to send traffic to any IP address other than the + * address associated with that transform will throw an IOException. In addition, if the + * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to + * send() or receive() until the transform is removed from the socket by calling {@link + * #removeTransportModeTransform(DatagramSocket, IpSecTransform)}; + * + * @param socket a datagram socket + * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. + */ + public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform) + throws IOException { + applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform); + } + + /* Call down to activate a transform */ + private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {} + + /** + * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to + * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to + * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic). + * Applications should probably not use this API directly. Instead, they should use {@link + * VpnService} to provide VPN capability in a more generic fashion. + * + * @param net a {@link Network} that will be tunneled via IP Sec. + * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform. + * @hide + */ + @SystemApi + public void applyTunnelModeTransform(Network net, IpSecTransform transform) {} + + /** + * Remove a transform from a given stream socket. Once removed, traffic on the socket will not + * be encypted. This allows sockets that have been used for IPsec to be reclaimed for + * communication in the clear in the event socket reuse is desired. This operation will succeed + * regardless of the underlying state of a transform. If a transform is removed, communication + * on all sockets to which that transform was applied will fail until this method is called. + * + * @param socket a socket that previously had a transform applied to it. + * @param transform the IPsec Transform that was previously applied to the given socket + */ + public void removeTransportModeTransform(Socket socket, IpSecTransform transform) { + removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform); + } + + /** + * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not + * be encypted. This allows sockets that have been used for IPsec to be reclaimed for + * communication in the clear in the event socket reuse is desired. This operation will succeed + * regardless of the underlying state of a transform. If a transform is removed, communication + * on all sockets to which that transform was applied will fail until this method is called. + * + * @param socket a socket that previously had a transform applied to it. + * @param transform the IPsec Transform that was previously applied to the given socket + */ + public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) { + removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform); + } + + /* Call down to activate a transform */ + private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {} + + /** + * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of + * cleanup if a tunneled Network experiences a change in default route. The Network will drop + * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is + * lost, all traffic will drop. + * + * @param net a network that currently has transform applied to it. + * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given + * network + * @hide + */ + @SystemApi + public void removeTunnelModeTransform(Network net, IpSecTransform transform) {} + + /** + * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for + * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic. + * + * <p>The socket provided by this class cannot be re-bound or closed via the inner + * FileDescriptor. Instead, disposing of this socket requires a call to close(). + */ + public static final class UdpEncapsulationSocket implements AutoCloseable { + private final FileDescriptor mFd; + private final Context mContext; + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private UdpEncapsulationSocket(Context context, int port) + throws ResourceUnavailableException { + mContext = context; + 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; + mCloseGuard.open("constructor"); + // TODO: go get a random socket on a random port + mFd = new FileDescriptor(); + } + + /** Access the inner UDP Encapsulation Socket */ + public FileDescriptor getSocket() { + return mFd; + } + + /** Retrieve the port number of the inner encapsulation socket */ + public int getPort() { + return 0; // TODO get the port number from the Socket; + } + + @Override + /** + * Release the resources that have been reserved for this Socket. + * + * <p>This method closes the underlying socket, reducing a user's allocated sockets in the + * system. This must be done as part of cleanup following use of a socket. Failure to do so + * will cause the socket to count against a total allocation limit for IpSec and eventually + * fail due to resource limits. + * + * @param fd a file descriptor previously returned as a UDP Encapsulation socket. + */ + public void close() { + // TODO: Go close the socket + mCloseGuard.close(); + } + + @Override + protected void finalize() throws Throwable { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + close(); + } + }; + + /** + * Open a socket that is bound to a free UDP port on the system. + * + * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by + * the caller. This provides safe access to a socket on a port that can later be used as a UDP + * Encapsulation port. + * + * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the + * socket port. Explicitly opening this port is only necessary if communication is desired on + * that port. + * + * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this + * method will bind to the specified port or fail. To retrieve the port number, call {@link + * android.system.Os#getsockname(FileDescriptor)}. + * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime + * of the object. + */ + // Returning a socket in this fashion that has been created and bound by the system + // is the only safe way to ensure that a socket is both accessible to the user and + // safely usable for Encapsulation without allowing a user to possibly unbind from/close + // the port, which could potentially impact the traffic of the next user who binds to that + // socket. + public UdpEncapsulationSocket openUdpEncapsulationSocket(int port) + throws IOException, ResourceUnavailableException { + // Temporary code + return new UdpEncapsulationSocket(mContext, port); + } + + /** + * Open a socket that is bound to a port selected by the system. + * + * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by + * the caller. This provides safe access to a socket on a port that can later be used as a UDP + * Encapsulation port. + * + * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the + * socket port. Explicitly opening this port is only necessary if communication is desired on + * that port. + * + * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port + */ + // Returning a socket in this fashion that has been created and bound by the system + // is the only safe way to ensure that a socket is both accessible to the user and + // safely usable for Encapsulation without allowing a user to possibly unbind from/close + // the port, which could potentially impact the traffic of the next user who binds to that + // socket. + public UdpEncapsulationSocket openUdpEncapsulationSocket() + throws IOException, ResourceUnavailableException { + // Temporary code + return new UdpEncapsulationSocket(mContext); + } + + /** + * Retrieve an instance of an IpSecManager within you application context + * + * @param context the application context for this manager + * @hide + */ + public IpSecManager(Context context, INetworkManagementService service) { + mContext = checkNotNull(context, "missing context"); + mService = checkNotNull(service, "missing service"); + } +} diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java new file mode 100644 index 000000000000..d6dd28bec390 --- /dev/null +++ b/core/java/android/net/IpSecTransform.java @@ -0,0 +1,471 @@ +/* + * 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.net; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.content.Context; +import android.system.ErrnoException; +import android.util.Log; +import dalvik.system.CloseGuard; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; + +/** + * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec. + * + * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout + * the lifetime of the underlying transform. If a transform object leaves scope, the underlying + * transform may be disabled automatically, with likely undesirable results. + * + * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array + * of traffic or may represent a transport mode transform operating on a Socket or Sockets. + */ +public final class IpSecTransform implements AutoCloseable { + private static final String TAG = "IpSecTransform"; + + /** + * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies + * to traffic towards the host. + */ + public static final int DIRECTION_IN = 0; + + /** + * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies + * to traffic from the host. + */ + public static final int DIRECTION_OUT = 1; + + /** @hide */ + @IntDef(value = {DIRECTION_IN, DIRECTION_OUT}) + @Retention(RetentionPolicy.SOURCE) + public @interface TransformDirection {} + + /** @hide */ + private static final int MODE_TUNNEL = 0; + + /** @hide */ + private static final int MODE_TRANSPORT = 1; + + /** @hide */ + public static final int ENCAP_NONE = 0; + + /** + * IpSec traffic will be encapsulated within UDP as per <a + * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>. + * + * @hide + */ + public static final int ENCAP_ESPINUDP = 1; + + /** + * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad + * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP. + * + * @hide + */ + public static final int ENCAP_ESPINUDP_NONIKE = 2; + + /** @hide */ + @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NONIKE}) + @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; + } + + 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(); + } + + startKeepalive(mContext); // Will silently fail if not required + mTransformId = transformId; + Log.d(TAG, "Added Transform with Id " + transformId); + } + mCloseGuard.open("build"); + + return this; + } + + /** + * Deactivate an IpSecTransform and free all resources for that transform that are managed by + * the system for this Transform. + * + * <p>Deactivating a transform while it is still applied to any Socket will result in sockets + * refusing to send or receive data. This method will silently succeed if the specified + * transform has already been removed; thus, it is always safe to attempt cleanup when a + * transform is no longer needed. + */ + public void close() { + Log.d(TAG, "Removing Transform with Id " + mTransformId); + + // Always safe to attempt cleanup + if (mTransformId == INVALID_TRANSFORM_ID) { + return; + } + //try { + stopKeepalive(); + //} catch (RemoteException e) { + // transform.setTransformId(transformId); + // throw e.rethrowFromSystemServer(); + //} finally { + mTransformId = INVALID_TRANSFORM_ID; + //} + mCloseGuard.close(); + } + + @Override + protected void finalize() throws Throwable { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /* Package */ + IpSecConfig getConfig() { + return mConfig; + } + + private final IpSecConfig mConfig; + private int mTransformId; + private final Context mContext; + private final CloseGuard mCloseGuard = CloseGuard.get(); + private ConnectivityManager.PacketKeepalive mKeepalive; + private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE; + private Object mKeepaliveSyncLock = new Object(); + private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback = + new ConnectivityManager.PacketKeepaliveCallback() { + + @Override + public void onStarted() { + synchronized (mKeepaliveSyncLock) { + mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS; + mKeepaliveSyncLock.notifyAll(); + } + } + + @Override + public void onStopped() { + synchronized (mKeepaliveSyncLock) { + mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE; + mKeepaliveSyncLock.notifyAll(); + } + } + + @Override + public void onError(int error) { + synchronized (mKeepaliveSyncLock) { + mKeepaliveStatus = error; + mKeepaliveSyncLock.notifyAll(); + } + } + }; + + /* Package */ + void startKeepalive(Context c) { + if (mConfig.getNattKeepaliveInterval() == 0) { + return; + } + + ConnectivityManager cm = + (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); + + if (mKeepalive != null) { + Log.e(TAG, "Keepalive already started for this IpSecTransform."); + return; + } + + synchronized (mKeepaliveSyncLock) { + mKeepalive = + cm.startNattKeepalive( + mConfig.getNetwork(), + mConfig.getNattKeepaliveInterval(), + mKeepaliveCallback, + mConfig.getLocalIp(), + mConfig.getEncapLocalPort(), + mConfig.getRemoteIp()); + try { + mKeepaliveSyncLock.wait(2000); + } catch (InterruptedException e) { + } + } + if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) { + throw new UnsupportedOperationException("Packet Keepalive cannot be started"); + } + } + + /* Package */ + void stopKeepalive() { + if (mKeepalive == null) { + return; + } + mKeepalive.stop(); + synchronized (mKeepaliveSyncLock) { + if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) { + try { + mKeepaliveSyncLock.wait(2000); + } catch (InterruptedException e) { + } + } + } + } + + /* Package */ + void setTransformId(int transformId) { + mTransformId = transformId; + } + + /* Package */ + int getTransformId() { + return mTransformId; + } + + /** + * Builder object to facilitate the creation of IpSecTransform objects. + * + * <p>Apply additional properties to the transform and then call a build() method to return an + * IpSecTransform object. + * + * @see Builder#buildTransportModeTransform(InetAddress) + */ + public static class Builder { + private Context mContext; + private IpSecConfig mConfig; + + /** + * Add an encryption algorithm to the transform for the given direction. + * + * <p>If encryption is set for a given direction without also providing an SPI for that + * direction, creation of an IpSecTransform will fail upon calling a build() method. + * + * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} + * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied. + */ + public IpSecTransform.Builder setEncryption( + @TransformDirection int direction, IpSecAlgorithm algo) { + mConfig.flow[direction].encryptionAlgo = algo; + return this; + } + + /** + * Add an authentication/integrity algorithm to the transform. + * + * <p>If authentication is set for a given direction without also providing an SPI for that + * direction, creation of an IpSecTransform will fail upon calling a build() method. + * + * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} + * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied. + */ + public IpSecTransform.Builder setAuthentication( + @TransformDirection int direction, IpSecAlgorithm algo) { + mConfig.flow[direction].authenticationAlgo = algo; + 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 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. + * + * <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 {@link IpSecManager.SecurityParameterIndex} to identify transformed + * traffic + */ + public IpSecTransform.Builder setSpi( + @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) { + mConfig.flow[direction].spi = spi.getSpi(); + return this; + } + + /** + * Specify the network on which this transform will emit its traffic; (otherwise it will + * emit on the default network). + * + * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in + * tunnel mode. + * + * @hide + */ + @SystemApi + public IpSecTransform.Builder setUnderlyingNetwork(Network net) { + mConfig.network = net; + return this; + } + + /** + * Add UDP encapsulation to an IPv4 transform + * + * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for + * details on how UDP should be applied to IPsec. + * + * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and + * receiving encapsulating traffic. + * @param remotePort the UDP port number of the remote that will send and receive + * encapsulated traffic. In the case of IKE, this is likely port 4500. + */ + public IpSecTransform.Builder setIpv4Encapsulation( + IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) { + // TODO: check encap type is valid. + mConfig.encapType = ENCAP_ESPINUDP; + mConfig.encapLocalPort = localSocket.getPort(); // TODO: plug in the encap socket + mConfig.encapRemotePort = remotePort; + return this; + } + + // TODO: Decrease the minimum keepalive to maybe 10? + // TODO: Probably a better exception to throw for NATTKeepalive failure + // TODO: Specify the needed NATT keepalive permission. + /** + * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded + * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot + * be activated, then the transform will fail to activate and throw an IOException. + * + * @param intervalSeconds the maximum number of seconds between keepalive packets, no less + * than 20s and no more than 3600s. + * @hide + */ + @SystemApi + public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) { + mConfig.nattKeepaliveInterval = intervalSeconds; + return this; + } + + /** + * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform. + * Some parameters have interdependencies that are checked at build time. If a well-formed + * transform cannot be created from the supplied parameters, this method will throw an + * Exception. + * + * <p>Upon a successful return from this call, the provided IpSecTransform will be active + * and may be applied to sockets. If too many IpSecTransform objects are active for a given + * user this operation will fail and throw ResourceUnavailableException. To avoid these + * exceptions, unused Transform objects must be cleaned up by calling {@link + * IpSecTransform#close()} when they are no longer needed. + * + * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this + * socket will cause the transform to be applied. + * <p>Note that an active transform will not impact any network traffic until it has + * been applied to one or more Sockets. Calling this method is a necessary precondition + * for applying it to a socket, but is not sufficient to actually apply IPsec. + * @throws IllegalArgumentException indicating that a particular combination of transform + * properties is invalid. + * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms + * may be allocated + * @throws SpiUnavailableException if the SPI collides with an existing transform + * (unlikely). + * @throws ResourceUnavailableException if the current user currently has exceeded the + * number of allowed active transforms. + */ + public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress) + throws IpSecManager.ResourceUnavailableException, + IpSecManager.SpiUnavailableException, IOException { + //FIXME: argument validation here + //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); + mConfig.mode = MODE_TRANSPORT; + mConfig.remoteAddress = remoteAddress; + return new IpSecTransform(mContext, mConfig).activate(); + } + + /** + * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some + * parameters have interdependencies that are checked at build time. + * + * @param localAddress the {@link InetAddress} that provides the local endpoint for this + * IPsec tunnel. This is almost certainly an address belonging to the {@link Network} + * that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}. + * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this + * IPsec tunnel. + * @throws IllegalArgumentException indicating that a particular combination of transform + * properties is invalid. + * @hide + */ + @SystemApi + public IpSecTransform buildTunnelModeTransform( + InetAddress localAddress, InetAddress remoteAddress) { + //FIXME: argument validation here + //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); + mConfig.localAddress = localAddress; + mConfig.remoteAddress = remoteAddress; + mConfig.mode = MODE_TUNNEL; + return new IpSecTransform(mContext, mConfig); + } + + /** + * Create a new IpSecTransform.Builder to construct an IpSecTransform + * + * @param context current Context + */ + public Builder(Context context) { + mContext = context; + mConfig = new IpSecConfig(); + } + } +} diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 1b715af0da29..43fab037f254 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -19,6 +19,7 @@ package android.net; import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.net.NetworkPolicy.CYCLE_NONE; +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -381,4 +382,20 @@ public class NetworkPolicyManager { string.append(")"); return string.toString(); } + + /** + * Returns true if {@param procState} is considered foreground and as such will be allowed + * to access network when the device is idle or in battery saver mode. Otherwise, false. + */ + public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { + return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; + } + + /** + * Returns true if {@param procState} is considered foreground and as such will be allowed + * to access network when the device is in data saver mode. Otherwise, false. + */ + public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) { + return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; + } } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 815d4807a39e..f0bea5b8e0ad 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -265,8 +265,8 @@ public class NetworkScoreManager { * the {@link #ACTION_CHANGE_ACTIVE} broadcast, or using a custom configuration activity. * * @return true if the operation succeeded, or false if the new package is not a valid scorer. - * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#SCORE_NETWORKS} permission. + * @throws SecurityException if the caller is not a system process or does not hold the + * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission * @hide */ @SystemApi diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 9ffe2fe7a709..734d89eb72af 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -211,7 +211,7 @@ public class BatteryManager { /** * Battery charge status, from a BATTERY_STATUS_* value. */ - public static final int BATTERY_PROPERTY_BATTERY_STATUS = 6; + public static final int BATTERY_PROPERTY_STATUS = 6; private final IBatteryStats mBatteryStats; private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ff86ff3ff076..c4a5be7f1ea6 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1121,7 +1121,7 @@ public final class Settings { public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS"; /** - * Activity Action: Show Zen Mode priority configuration settings. + * Activity Action: Show Zen Mode (aka Do Not Disturb) priority configuration settings. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_ZEN_MODE_PRIORITY_SETTINGS diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java index c8358a6c4677..3e992ec36426 100644 --- a/core/java/android/service/notification/ConditionProviderService.java +++ b/core/java/android/service/notification/ConditionProviderService.java @@ -127,7 +127,7 @@ public abstract class ConditionProviderService extends Service { } /** - * Request that the provider be rebound, after a previous call to (@link requestUnbind). + * Request that the provider be rebound, after a previous call to (@link #requestUnbind). * * <p>This method will fail for providers that have not been granted the permission by the user. */ diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 8a83b7aa8373..70e0461ce73f 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -148,7 +148,7 @@ public abstract class NotificationListenerService extends Service { // Notification cancellation reasons - /** Notification was canceled by the status bar reporting a click. */ + /** Notification was canceled by the status bar reporting a notification click. */ public static final int REASON_DELEGATE_CLICK = 1; /** Notification was canceled by the status bar reporting a user dismissal. */ public static final int REASON_DELEGATE_CANCEL = 2; @@ -636,7 +636,7 @@ public abstract class NotificationListenerService extends Service { * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * - * @return An array of active notifications, sorted in natural order. + * @return An array of snoozed notifications, sorted in natural order. */ public final StatusBarNotification[] getSnoozedNotifications() { try { diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 85bccf746305..6a24aa454550 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -67,6 +67,9 @@ public class StatusBarNotification implements Parcelable { this.groupKey = groupKey(); } + /** + * @deprecated Non-system apps should not need to create StatusBarNotifications. + */ @Deprecated public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user, diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index b718696b2202..0ac16c1fa023 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -95,6 +95,11 @@ public class SurfaceControl { IBinder displayToken, int mode); private static native void nativeDeferTransactionUntil(long nativeObject, IBinder handle, long frame); + private static native void nativeDeferTransactionUntilSurface(long nativeObject, + long surfaceObject, long frame); + private static native void nativeReparentChildren(long nativeObject, + IBinder handle); + private static native void nativeSeverChildren(long nativeObject); private static native void nativeSetOverrideScalingMode(long nativeObject, int scalingMode); private static native IBinder nativeGetHandle(long nativeObject); @@ -421,6 +426,18 @@ public class SurfaceControl { nativeDeferTransactionUntil(mNativeObject, handle, frame); } + public void deferTransactionUntil(Surface barrier, long frame) { + nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame); + } + + public void reparentChildren(IBinder newParentHandle) { + nativeReparentChildren(mNativeObject, newParentHandle); + } + + public void detachChildren() { + nativeSeverChildren(mNativeObject); + } + public void setOverrideScalingMode(int scalingMode) { checkNotReleased(); nativeSetOverrideScalingMode(mNativeObject, scalingMode); diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java index 3cf5af484625..b5912bc1e1c8 100644 --- a/core/java/android/view/SurfaceSession.java +++ b/core/java/android/view/SurfaceSession.java @@ -27,6 +27,7 @@ public final class SurfaceSession { private long mNativeClient; // SurfaceComposerClient* private static native long nativeCreate(); + private static native long nativeCreateScoped(long surfacePtr); private static native void nativeDestroy(long ptr); private static native void nativeKill(long ptr); @@ -35,6 +36,10 @@ public final class SurfaceSession { mNativeClient = nativeCreate(); } + public SurfaceSession(Surface root) { + mNativeClient = nativeCreateScoped(root.mNativeObject); + } + /* no user serviceable parts here ... */ @Override protected void finalize() throws Throwable { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d2577d48c3d1..64306338311b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,6 +16,10 @@ package android.view; +import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER; +import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER; +import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER; + import android.content.Context; import android.content.res.CompatibilityInfo.Translator; import android.content.res.Configuration; @@ -26,16 +30,12 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Handler; import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; -import com.android.internal.view.BaseIWindow; import com.android.internal.view.SurfaceCallbackHelper; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; @@ -92,8 +92,8 @@ import java.util.concurrent.locks.ReentrantLock; * positioned asynchronously.</p> */ public class SurfaceView extends View { - static private final String TAG = "SurfaceView"; - static private final boolean DEBUG = false; + private static final String TAG = "SurfaceView"; + private static final boolean DEBUG = false; final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<SurfaceHolder.Callback>(); @@ -102,28 +102,23 @@ public class SurfaceView extends View { final ReentrantLock mSurfaceLock = new ReentrantLock(); final Surface mSurface = new Surface(); // Current surface in use - final Surface mNewSurface = new Surface(); // New surface we are switching to boolean mDrawingStopped = true; + // We use this to track if the application has produced a frame + // in to the Surface. Up until that point, we should be careful not to punch + // holes. + boolean mDrawFinished = false; + + final Rect mScreenRect = new Rect(); + SurfaceSession mSurfaceSession; - final WindowManager.LayoutParams mLayout - = new WindowManager.LayoutParams(); - IWindowSession mSession; - MyWindow mWindow; - final Rect mVisibleInsets = new Rect(); - final Rect mWinFrame = new Rect(); - final Rect mOverscanInsets = new Rect(); - final Rect mContentInsets = new Rect(); - final Rect mStableInsets = new Rect(); - final Rect mOutsets = new Rect(); - final Rect mBackdropFrame = new Rect(); + SurfaceControl mSurfaceControl; final Rect mTmpRect = new Rect(); final Configuration mConfiguration = new Configuration(); static final int KEEP_SCREEN_ON_MSG = 1; - static final int GET_NEW_SURFACE_MSG = 2; - static final int UPDATE_WINDOW_MSG = 3; + static final int DRAW_FINISHED_MSG = 2; - int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + int mSubLayer = APPLICATION_MEDIA_SUBLAYER; boolean mIsCreating = false; private volatile boolean mRtHandlingPositionUpdates = false; @@ -135,11 +130,9 @@ public class SurfaceView extends View { case KEEP_SCREEN_ON_MSG: { setKeepScreenOn(msg.arg1 != 0); } break; - case GET_NEW_SURFACE_MSG: { - handleGetNewSurface(); - } break; - case UPDATE_WINDOW_MSG: { - updateWindow(); + case DRAW_FINISHED_MSG: { + mDrawFinished = true; + invalidate(); } break; } } @@ -149,7 +142,7 @@ public class SurfaceView extends View { = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { - updateWindow(); + updateSurface(); } }; @@ -159,13 +152,14 @@ public class SurfaceView extends View { public boolean onPreDraw() { // reposition ourselves where the surface is mHaveFrame = getWidth() > 0 && getHeight() > 0; - updateWindow(); + updateSurface(); return true; } }; boolean mRequestedVisible = false; boolean mWindowVisibility = false; + boolean mLastWindowVisibility = false; boolean mViewVisibility = false; int mRequestedWidth = -1; int mRequestedHeight = -1; @@ -181,19 +175,17 @@ public class SurfaceView extends View { boolean mVisible = false; int mWindowSpaceLeft = -1; int mWindowSpaceTop = -1; - int mWindowSpaceWidth = -1; - int mWindowSpaceHeight = -1; + int mSurfaceWidth = -1; + int mSurfaceHeight = -1; int mFormat = -1; final Rect mSurfaceFrame = new Rect(); int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; - boolean mUpdateWindowNeeded; - boolean mReportDrawNeeded; private Translator mTranslator; - private int mWindowInsetLeft; - private int mWindowInsetTop; private boolean mGlobalListenersAdded; + private int mSurfaceFlags = SurfaceControl.HIDDEN; + public SurfaceView(Context context) { this(context, null); } @@ -227,11 +219,8 @@ public class SurfaceView extends View { protected void onAttachedToWindow() { super.onAttachedToWindow(); mParent.requestTransparentRegion(this); - mSession = getWindowSession(); - mLayout.token = getWindowToken(); - mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle()); - mLayout.packageName = mContext.getOpPackageName(); mViewVisibility = getVisibility() == VISIBLE; + mRequestedVisible = mViewVisibility && mWindowVisibility; if (!mGlobalListenersAdded) { ViewTreeObserver observer = getViewTreeObserver(); @@ -246,7 +235,7 @@ public class SurfaceView extends View { super.onWindowVisibilityChanged(visibility); mWindowVisibility = visibility == VISIBLE; mRequestedVisible = mWindowVisibility && mViewVisibility; - updateWindow(); + updateSurface(); } @Override @@ -264,7 +253,7 @@ public class SurfaceView extends View { requestLayout(); } mRequestedVisible = newRequestedVisible; - updateWindow(); + updateSurface(); } @Override @@ -277,19 +266,14 @@ public class SurfaceView extends View { } mRequestedVisible = false; - updateWindow(); - mHaveFrame = false; - if (mWindow != null) { - try { - mSession.remove(mWindow); - } catch (RemoteException ex) { - // Not much we can do here... - } - mWindow = null; + + updateSurface(); + if (mSurfaceControl != null) { + mSurfaceControl.destroy(); } - mSession = null; - mLayout.token = null; + mSurfaceControl = null; + mHaveFrame = false; super.onDetachedFromWindow(); } @@ -308,13 +292,13 @@ public class SurfaceView extends View { @Override protected boolean setFrame(int left, int top, int right, int bottom) { boolean result = super.setFrame(left, top, right, bottom); - updateWindow(); + updateSurface(); return result; } @Override public boolean gatherTransparentRegion(Region region) { - if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + if (isAboveParent()) { return super.gatherTransparentRegion(region); } @@ -341,7 +325,7 @@ public class SurfaceView extends View { @Override public void draw(Canvas canvas) { - if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + if (mDrawFinished && !isAboveParent()) { // draw() is not called when SKIP_DRAW is set if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { // punch a whole in the view-hierarchy below us @@ -353,8 +337,8 @@ public class SurfaceView extends View { @Override protected void dispatchDraw(Canvas canvas) { - if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - // if SKIP_DRAW is cleared, draw() has already punched a hole + if (mDrawFinished && !isAboveParent()) { + // draw() is not called when SKIP_DRAW is set if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { // punch a whole in the view-hierarchy below us canvas.drawColor(0, PorterDuff.Mode.CLEAR); @@ -375,9 +359,8 @@ public class SurfaceView extends View { * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. */ public void setZOrderMediaOverlay(boolean isMediaOverlay) { - mWindowType = isMediaOverlay - ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY - : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + mSubLayer = isMediaOverlay + ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER; } /** @@ -395,12 +378,9 @@ public class SurfaceView extends View { */ public void setZOrderOnTop(boolean onTop) { if (onTop) { - mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; - // ensures the surface is placed below the IME - mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mSubLayer = APPLICATION_PANEL_SUBLAYER; } else { - mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; - mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mSubLayer = APPLICATION_MEDIA_SUBLAYER; } } @@ -418,31 +398,23 @@ public class SurfaceView extends View { */ public void setSecure(boolean isSecure) { if (isSecure) { - mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE; + mSurfaceFlags |= SurfaceControl.SECURE; } else { - mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; + mSurfaceFlags &= ~SurfaceControl.SECURE; } } - /** - * Hack to allow special layering of windows. The type is one of the - * types in WindowManager.LayoutParams. This is a hack so: - * @hide - */ - public void setWindowType(int type) { - mWindowType = type; - } - /** @hide */ - protected void updateWindow() { + protected void updateSurface() { if (!mHaveFrame) { return; } ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot != null) { - mTranslator = viewRoot.mTranslator; + if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) { + return; } + mTranslator = viewRoot.mTranslator; if (mTranslator != null) { mSurface.setCompatibilityTranslator(mTranslator); } @@ -452,17 +424,15 @@ public class SurfaceView extends View { int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; - final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight; + final boolean creating = (mSurfaceControl == null || formatChanged) + && mRequestedVisible; + final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; - final boolean layoutSizeChanged = getWidth() != mLayout.width - || getHeight() != mLayout.height; - + final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility; boolean redrawNeeded = false; - if (creating || formatChanged || sizeChanged || visibleChanged - || mUpdateWindowNeeded || mReportDrawNeeded) { + if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) { getLocationInWindow(mLocation); if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " @@ -476,93 +446,74 @@ public class SurfaceView extends View { final boolean visible = mVisible = mRequestedVisible; mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; - mWindowSpaceWidth = myWidth; - mWindowSpaceHeight = myHeight; + mSurfaceWidth = myWidth; + mSurfaceHeight = myHeight; mFormat = mRequestedFormat; + mLastWindowVisibility = mWindowVisibility; - // Scaling/Translate window's layout here because mLayout is not used elsewhere. - - // Places the window relative - mLayout.x = mWindowSpaceLeft; - mLayout.y = mWindowSpaceTop; - mLayout.width = getWidth(); - mLayout.height = getHeight(); + mScreenRect.left = mWindowSpaceLeft; + mScreenRect.top = mWindowSpaceTop; + mScreenRect.right = mWindowSpaceLeft + getWidth(); + mScreenRect.bottom = mWindowSpaceTop + getHeight(); if (mTranslator != null) { - mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); + mTranslator.translateRectInAppWindowToScreen(mScreenRect); } - mLayout.format = mRequestedFormat; - mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_SCALED - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - ; - if (!creating && !sizeChanged) { - mLayout.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; - } else { - mLayout.privateFlags &= - ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; + if (creating) { + mSurfaceSession = new SurfaceSession(viewRoot.mSurface); + mSurfaceControl = new SurfaceControl(mSurfaceSession, + "SurfaceView - " + viewRoot.getTitle().toString(), + mSurfaceWidth, mSurfaceHeight, mFormat, + mSurfaceFlags); } - if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { - mLayout.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; - } - mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION - | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; - - if (mWindow == null) { - Display display = getDisplay(); - mWindow = new MyWindow(this); - mLayout.type = mWindowType; - mLayout.gravity = Gravity.START|Gravity.TOP; - mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout, - mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets, - mStableInsets); - } - - boolean realSizeChanged; - boolean reportDrawNeeded; - - int relayoutResult; + boolean realSizeChanged = false; mSurfaceLock.lock(); try { - mUpdateWindowNeeded = false; - reportDrawNeeded = mReportDrawNeeded; - mReportDrawNeeded = false; mDrawingStopped = !visible; if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Cur surface: " + mSurface); - relayoutResult = mSession.relayout( - mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight, - visible ? VISIBLE : GONE, - WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY, - mWinFrame, mOverscanInsets, mContentInsets, - mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, - mConfiguration, mNewSurface); - if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { - reportDrawNeeded = true; + SurfaceControl.openTransaction(); + try { + mSurfaceControl.setLayer(mSubLayer); + if (mViewVisibility) { + mSurfaceControl.show(); + } else { + mSurfaceControl.hide(); + } + + // While creating the surface, we will set it's initial + // geometry. Outside of that though, we should generally + // leave it to the RenderThread. + if (creating || !mRtHandlingPositionUpdates) { + mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top); + mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth, + 0.0f, 0.0f, + mScreenRect.height() / (float) mSurfaceHeight); + } + if (sizeChanged) { + mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight); + } + } finally { + SurfaceControl.closeTransaction(); } - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "New surface: " + mNewSurface - + ", vis=" + visible + ", frame=" + mWinFrame); + if (sizeChanged || creating) { + redrawNeeded = true; + } mSurfaceFrame.left = 0; mSurfaceFrame.top = 0; if (mTranslator == null) { - mSurfaceFrame.right = mWinFrame.width(); - mSurfaceFrame.bottom = mWinFrame.height(); + mSurfaceFrame.right = mSurfaceWidth; + mSurfaceFrame.bottom = mSurfaceHeight; } else { float appInvertedScale = mTranslator.applicationInvertedScale; - mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); - mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); + mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); + mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); } final int surfaceWidth = mSurfaceFrame.right; @@ -576,12 +527,11 @@ public class SurfaceView extends View { } try { - redrawNeeded |= creating | reportDrawNeeded; + redrawNeeded |= visible && !mDrawFinished; SurfaceHolder.Callback callbacks[] = null; - final boolean surfaceChanged = (relayoutResult - & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0; + final boolean surfaceChanged = creating; if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { mSurfaceCreated = false; if (mSurface.isValid()) { @@ -608,7 +558,10 @@ public class SurfaceView extends View { } } - mSurface.transferFrom(mNewSurface); + if (creating) { + mSurface.copyFrom(mSurfaceControl); + } + if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; @@ -641,53 +594,57 @@ public class SurfaceView extends View { callbacks = getSurfaceCallbacks(); } SurfaceCallbackHelper sch = - new SurfaceCallbackHelper(mSession, mWindow); + new SurfaceCallbackHelper(this::onDrawFinished); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } } finally { mIsCreating = false; - mSession.performDeferredDestroy(mWindow); + if (mSurfaceControl != null && !mSurfaceCreated) { + mSurfaceControl.destroy(); + mSurfaceControl = null; + } } - } catch (RemoteException ex) { + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } if (DEBUG) Log.v( - TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + - " w=" + mLayout.width + " h=" + mLayout.height + - ", frame=" + mSurfaceFrame); + TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top + + " w=" + mScreenRect.width() + " h=" + mScreenRect.height() + + ", frame=" + mSurfaceFrame); } else { // Calculate the window position in case RT loses the window // and we need to fallback to a UI-thread driven position update getLocationInWindow(mLocation); final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1]; + final boolean layoutSizeChanged = getWidth() != mScreenRect.width() + || getHeight() != mScreenRect.height(); if (positionChanged || layoutSizeChanged) { // Only the position has changed mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; - // For our size changed check, we keep mLayout.width and mLayout.height + // For our size changed check, we keep mScreenRect.width() and mScreenRect.height() // in view local space. - mLocation[0] = mLayout.width = getWidth(); - mLocation[1] = mLayout.height = getHeight(); + mLocation[0] = getWidth(); + mLocation[1] = getHeight(); transformFromViewToWindowSpace(mLocation); - mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop, + mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop, mLocation[0], mLocation[1]); if (mTranslator != null) { - mTranslator.translateRectInAppWindowToScreen(mTmpRect); + mTranslator.translateRectInAppWindowToScreen(mScreenRect); } if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) { try { - if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " + + if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom)); - mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom, -1, mTmpRect); - } catch (RemoteException ex) { + mScreenRect.left, mScreenRect.top, + mScreenRect.right, mScreenRect.bottom)); + setParentSpaceRectangle(mScreenRect, -1); + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } } @@ -695,18 +652,40 @@ public class SurfaceView extends View { } } + private void onDrawFinished() { + if (DEBUG) { + Log.i(TAG, System.identityHashCode(this) + " " + + "finishedDrawing"); + } + mHandler.sendEmptyMessage(DRAW_FINISHED_MSG); + } + + private void setParentSpaceRectangle(Rect position, long frameNumber) { + ViewRootImpl viewRoot = getViewRootImpl(); + + SurfaceControl.openTransaction(); + try { + if (frameNumber > 0) { + mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber); + } + mSurfaceControl.setPosition(position.left, position.top); + mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth, + 0.0f, 0.0f, + position.height() / (float) mSurfaceHeight); + } finally { + SurfaceControl.closeTransaction(); + } + } + private Rect mRTLastReportedPosition = new Rect(); /** * Called by native by a Rendering Worker thread to update the window position * @hide */ - public final void updateWindowPosition_renderWorker(long frameNumber, + public final void updateSurfacePosition_renderWorker(long frameNumber, int left, int top, int right, int bottom) { - IWindowSession session = mSession; - MyWindow window = mWindow; - if (session == null || window == null) { - // Guess we got detached, that sucks + if (mSurfaceControl == null) { return; } // TODO: This is teensy bit racey in that a brand new SurfaceView moving on @@ -726,35 +705,29 @@ public class SurfaceView extends View { } try { if (DEBUG) { - Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " + + Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), frameNumber, left, top, right, bottom)); } - // Just using mRTLastReportedPosition as a dummy rect here - session.repositionChild(window, left, top, right, bottom, - frameNumber, - mRTLastReportedPosition); - // Now overwrite mRTLastReportedPosition with our values mRTLastReportedPosition.set(left, top, right, bottom); - } catch (RemoteException ex) { + setParentSpaceRectangle(mRTLastReportedPosition, frameNumber); + // Now overwrite mRTLastReportedPosition with our values + } catch (Exception ex) { Log.e(TAG, "Exception from repositionChild", ex); } } /** - * Called by native on RenderThread to notify that the window is no longer in the + * Called by native on RenderThread to notify that the view is no longer in the * draw tree. UI thread is blocked at this point. * @hide */ - public final void windowPositionLost_uiRtSync(long frameNumber) { + public final void surfacePositionLost_uiRtSync(long frameNumber) { if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); } - IWindowSession session = mSession; - MyWindow window = mWindow; - if (session == null || window == null) { - // We got detached prior to receiving this, abort + if (mSurfaceControl == null) { return; } if (mRtHandlingPositionUpdates) { @@ -763,19 +736,14 @@ public class SurfaceView extends View { // safely access other member variables at this time. // So do what the UI thread would have done if RT wasn't handling position // updates. - mTmpRect.set(mLayout.x, mLayout.y, - mLayout.x + mLayout.width, - mLayout.y + mLayout.height); - - if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) { + if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { try { - if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " + + if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom)); - session.repositionChild(window, mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame); - } catch (RemoteException ex) { + mScreenRect.left, mScreenRect.top, + mScreenRect.right, mScreenRect.bottom)); + setParentSpaceRectangle(mScreenRect, frameNumber); + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } } @@ -792,10 +760,6 @@ public class SurfaceView extends View { return callbacks; } - void handleGetNewSurface() { - updateWindow(); - } - /** * Check to see if the surface has fixed size dimensions or if the surface's * dimensions are dimensions are dependent on its current layout. @@ -807,65 +771,8 @@ public class SurfaceView extends View { return (mRequestedWidth != -1 || mRequestedHeight != -1); } - private static class MyWindow extends BaseIWindow { - private final WeakReference<SurfaceView> mSurfaceView; - - public MyWindow(SurfaceView surfaceView) { - mSurfaceView = new WeakReference<SurfaceView>(surfaceView); - } - - @Override - public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig, Rect backDropRect, boolean forceLayout, - boolean alwaysConsumeNavBar, int displayId) { - SurfaceView surfaceView = mSurfaceView.get(); - if (surfaceView != null) { - if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width() - + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight); - surfaceView.mSurfaceLock.lock(); - try { - if (reportDraw) { - surfaceView.mUpdateWindowNeeded = true; - surfaceView.mReportDrawNeeded = true; - surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); - } else if (surfaceView.mWinFrame.width() != frame.width() - || surfaceView.mWinFrame.height() != frame.height() - || forceLayout) { - surfaceView.mUpdateWindowNeeded = true; - surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); - } - } finally { - surfaceView.mSurfaceLock.unlock(); - } - } - } - - @Override - public void dispatchAppVisibility(boolean visible) { - // The point of SurfaceView is to let the app control the surface. - } - - @Override - public void dispatchGetNewSurface() { - SurfaceView surfaceView = mSurfaceView.get(); - if (surfaceView != null) { - Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); - surfaceView.mHandler.sendMessage(msg); - } - } - - @Override - public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { - Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); - } - - @Override - public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { - } - - int mCurWidth = -1; - int mCurHeight = -1; + private boolean isAboveParent() { + return mSubLayer >= 0; } private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { @@ -913,15 +820,14 @@ public class SurfaceView extends View { @Override public void setFormat(int format) { - // for backward compatibility reason, OPAQUE always // means 565 for SurfaceView if (format == PixelFormat.OPAQUE) format = PixelFormat.RGB_565; mRequestedFormat = format; - if (mWindow != null) { - updateWindow(); + if (mSurfaceControl != null) { + updateSurface(); } } @@ -982,10 +888,10 @@ public class SurfaceView extends View { mSurfaceLock.lock(); if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped=" - + mDrawingStopped + ", win=" + mWindow); + + mDrawingStopped + ", surfaceControl=" + mSurfaceControl); Canvas c = null; - if (!mDrawingStopped && mWindow != null) { + if (!mDrawingStopped && mSurfaceControl != null) { try { if (hardware) { c = mSurface.lockHardwareCanvas(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 20d960fff661..f9863b0a6761 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2632,6 +2632,14 @@ public final class ViewRootImpl implements ViewParent, } } + private void onDrawFinished() { + try { + mWindowSession.finishDrawing(mWindow); + } catch (RemoteException e) { + // Have fun! + } + } + private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; @@ -2682,7 +2690,7 @@ public final class ViewRootImpl implements ViewParent, } if (mSurfaceHolder != null && mSurface.isValid()) { - SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index adc6f725f42e..493774038e4e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10335,7 +10335,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) text, start, end); // Make sure selection mode is engaged. if (mEditor != null) { - mEditor.startSelectionActionModeAsync(); + mEditor.startSelectionActionMode(); } return true; } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 3a0906393b97..94bd342c9b9b 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -813,8 +813,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { private final OnValueSelectedListener mOnValueSelectedListener = new OnValueSelectedListener() { @Override public void onValueSelected(int pickerType, int newValue, boolean autoAdvance) { + boolean valueChanged = false; switch (pickerType) { case RadialTimePickerView.HOURS: + if (getHour() != newValue) { + valueChanged = true; + } final boolean isTransition = mAllowAutoAdvance && autoAdvance; setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition); if (isTransition) { @@ -825,11 +829,14 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { } break; case RadialTimePickerView.MINUTES: + if (getMinute() != newValue) { + valueChanged = true; + } setMinuteInternal(newValue, FROM_RADIAL_PICKER); break; } - if (mOnTimeChangedListener != null) { + if (mOnTimeChangedListener != null && valueChanged) { mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute()); } } diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java new file mode 100644 index 000000000000..c840f26a0d53 --- /dev/null +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.internal.notification; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.provider.Settings; + +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// Manages the NotificationChannels used by the frameworks itself. +public class SystemNotificationChannels { + public static String VIRTUAL_KEYBOARD = "VIRTUAL_KEYBOARD"; + public static String PHYSICAL_KEYBOARD = "PHYSICAL_KEYBOARD"; + public static String SECURITY = "SECURITY"; + public static String CAR_MODE = "CAR_MODE"; + public static String ACCOUNT = "ACCOUNT"; + public static String DEVELOPER = "DEVELOPER"; + public static String UPDATES = "UPDATES"; + public static String NETWORK_STATUS = "NETWORK_STATUS"; + public static String NETWORK_ALERTS = "NETWORK_ALERTS"; + public static String VPN = "VPN"; + public static String DEVICE_ADMIN = "DEVICE_ADMIN"; + public static String ALERTS = "ALERTS"; + public static String RETAIL_MODE = "RETAIL_MODE"; + public static String USB = "USB"; + + public static void createAll(Context context) { + final NotificationManager nm = context.getSystemService(NotificationManager.class); + List<NotificationChannel> channelsList = new ArrayList<NotificationChannel>(); + channelsList.add(new NotificationChannel( + VIRTUAL_KEYBOARD, + context.getString(R.string.notification_channel_virtual_keyboard), + NotificationManager.IMPORTANCE_LOW)); + + final NotificationChannel physicalKeyboardChannel = new NotificationChannel( + PHYSICAL_KEYBOARD, + context.getString(R.string.notification_channel_physical_keyboard), + NotificationManager.IMPORTANCE_DEFAULT); + physicalKeyboardChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, + Notification.AUDIO_ATTRIBUTES_DEFAULT); + channelsList.add(physicalKeyboardChannel); + + channelsList.add(new NotificationChannel( + SECURITY, + context.getString(R.string.notification_channel_security), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + CAR_MODE, + context.getString(R.string.notification_channel_car_mode), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + DEVELOPER, + context.getString(R.string.notification_channel_developer), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + UPDATES, + context.getString(R.string.notification_channel_updates), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + NETWORK_STATUS, + context.getString(R.string.notification_channel_network_status), + NotificationManager.IMPORTANCE_LOW)); + + final NotificationChannel networkAlertsChannel = new NotificationChannel( + NETWORK_ALERTS, + context.getString(R.string.notification_channel_network_alerts), + NotificationManager.IMPORTANCE_HIGH); + networkAlertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, + Notification.AUDIO_ATTRIBUTES_DEFAULT); + channelsList.add(networkAlertsChannel); + + channelsList.add(new NotificationChannel( + VPN, + context.getString(R.string.notification_channel_vpn), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + DEVICE_ADMIN, + context.getString(R.string.notification_channel_device_admin), + NotificationManager.IMPORTANCE_LOW)); + + final NotificationChannel alertsChannel = new NotificationChannel( + ALERTS, + context.getString(R.string.notification_channel_alerts), + NotificationManager.IMPORTANCE_DEFAULT); + alertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, + Notification.AUDIO_ATTRIBUTES_DEFAULT); + channelsList.add(alertsChannel); + + channelsList.add(new NotificationChannel( + RETAIL_MODE, + context.getString(R.string.notification_channel_retail_mode), + NotificationManager.IMPORTANCE_LOW)); + + channelsList.add(new NotificationChannel( + USB, + context.getString(R.string.notification_channel_usb), + NotificationManager.IMPORTANCE_MIN)); + + nm.createNotificationChannels(channelsList); + createAccountChannelForPackage(context.getPackageName(), context); + } + + public static void createAccountChannelForPackage(String pkg, Context context) { + final NotificationManager nm = context.getSystemService(NotificationManager.class); + nm.createNotificationChannelsForPackage(pkg, Arrays.asList(new NotificationChannel( + ACCOUNT, + context.getString(R.string.notification_channel_account), + NotificationManager.IMPORTANCE_LOW))); + } + + private SystemNotificationChannels() {} +} diff --git a/core/java/com/android/internal/util/ParcelableString.java b/core/java/com/android/internal/util/ParcelableString.java deleted file mode 100644 index 6bd856f5521d..000000000000 --- a/core/java/com/android/internal/util/ParcelableString.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.util; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Helper class to adapt a simple String to cases where a Parcelable is expected. - * @hide - */ -public class ParcelableString implements Parcelable { - public String string; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(string); - } - - public static final Parcelable.Creator<ParcelableString> CREATOR = - new Parcelable.Creator<ParcelableString>() { - @Override - public ParcelableString createFromParcel(Parcel in) { - ParcelableString ret = new ParcelableString(); - ret.string = in.readString(); - return ret; - } - @Override - public ParcelableString[] newArray(int size) { - return new ParcelableString[size]; - } - }; -}
\ No newline at end of file diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java index 5b6a82cf1c43..507b673ec279 100644 --- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java +++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java @@ -17,14 +17,11 @@ package com.android.internal.view; import android.os.RemoteException; -import android.view.IWindow; -import android.view.IWindowSession; import android.view.Surface; import android.view.SurfaceHolder; public class SurfaceCallbackHelper { - IWindowSession mSession; - IWindow.Stub mWindow; + Runnable mRunnable; int mFinishDrawingCollected = 0; int mFinishDrawingExpected = 0; @@ -37,26 +34,18 @@ public class SurfaceCallbackHelper { if (mFinishDrawingCollected < mFinishDrawingExpected) { return; } - try { - mSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } + mRunnable.run(); } } }; - public SurfaceCallbackHelper(IWindowSession session, - IWindow.Stub window) { - mSession = session; - mWindow = window; + public SurfaceCallbackHelper(Runnable callbacksCollected) { + mRunnable = callbacksCollected; } public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) { if (callbacks == null || callbacks.length == 0) { - try { - mSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } + mRunnable.run(); return; } diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index 69e974c672d0..1de0af6f31df 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -388,14 +388,22 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT; mLastPosition = nextMenuPosition; - final int[] tempLocation = new int[2]; - - // This popup menu will be positioned relative to the top-left edge - // of the view representing its parent menu. - parentView.getLocationInWindow(tempLocation); - final int parentOffsetLeft = parentInfo.window.getHorizontalOffset() + tempLocation[0]; - final int parentOffsetTop = parentInfo.window.getVerticalOffset() + tempLocation[1]; - + // A popup anchored to mAnchorView with (0,0) offset would be shown at this position. + final int[] offsetOrigin = new int[2]; + mAnchorView.getLocationOnScreen(offsetOrigin); + offsetOrigin[1] += mAnchorView.getHeight(); + + final int[] parentViewScreenLocation = new int[2]; + parentView.getLocationOnScreen(parentViewScreenLocation); + + // Translate the parent view location into the offset coordinate space. + // If used as horizontal/vertical offsets, these values would position the submenu + // at the exact same position as the parent item. + final int parentOffsetLeft = parentViewScreenLocation[0] - offsetOrigin[0]; + final int parentOffsetTop = parentViewScreenLocation[1] - offsetOrigin[1]; + + // Adjust the horizontal offset to display the submenu to the right or to the left + // of the parent item. // By now, mDropDownGravity is the resolved absolute gravity, so // this should work in both LTR and RTL. final int x; @@ -412,11 +420,10 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey x = parentOffsetLeft - menuWidth; } } - popupWindow.setHorizontalOffset(x); - final int y = parentOffsetTop; - popupWindow.setVerticalOffset(y); + // Use the same vertical offset as the parent item. + popupWindow.setVerticalOffset(parentOffsetTop); } else { if (mHasXOffset) { popupWindow.setHorizontalOffset(mXOffset); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 3ca455dc24cf..c348cc581c81 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -643,7 +643,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, if (!bitmap.get()) { return NULL; } - return createBitmap(env, bitmap.release(), kBitmapCreateFlag_None); + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable)); } SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); @@ -1306,7 +1306,7 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit doThrowRE(env, "Could not copy a hardware bitmap."); return NULL; } - return createBitmap(env, allocator.getStorageObjAndReset(), kBitmapCreateFlag_None); + return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false)); } static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) { @@ -1316,7 +1316,7 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic ALOGW("failed to create hardware bitmap from graphic buffer"); return NULL; } - return bitmap::createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_None); + return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); } static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) { diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index c7998a169225..1c6ead0f1086 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -111,11 +111,12 @@ void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChan } static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, - NativeInputChannel* nativeInputChannel) { + std::unique_ptr<NativeInputChannel> nativeInputChannel) { jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.ctor); if (inputChannelObj) { - android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); + android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, + nativeInputChannel.release()); } return inputChannelObj; } @@ -143,13 +144,13 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* } jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, - new NativeInputChannel(serverChannel)); + std::make_unique<NativeInputChannel>(serverChannel)); if (env->ExceptionCheck()) { return NULL; } jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, - new NativeInputChannel(clientChannel)); + std::make_unique<NativeInputChannel>(clientChannel)); if (env->ExceptionCheck()) { return NULL; } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index f221392f16bd..edcbb3f783c3 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -627,9 +627,9 @@ static const JNINativeMethod gMethods[] = { int register_android_view_RenderNode(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/view/SurfaceView"); gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz, - "updateWindowPosition_renderWorker", "(JIIII)V"); + "updateSurfacePosition_renderWorker", "(JIIII)V"); gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz, - "windowPositionLost_uiRtSync", "(J)V"); + "surfacePositionLost_uiRtSync", "(J)V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a81901df9a1b..be86f5c6b8ab 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -693,7 +693,6 @@ static jboolean nativeGetAnimationFrameStats(JNIEnv* env, jclass clazz, jobject return JNI_TRUE; } - static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject, jobject handleObject, jlong frameNumber) { auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); @@ -702,6 +701,27 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeO ctrl->deferTransactionUntil(handle, frameNumber); } +static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject, + jobject surfaceObject, jlong frameNumber) { + auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject); + + ctrl->deferTransactionUntil(barrier, frameNumber); +} + +static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject, + jobject newParentObject) { + auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + sp<IBinder> handle = ibinderForJavaObject(env, newParentObject); + + ctrl->reparentChildren(handle); +} + +static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) { + auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + ctrl->detachChildren(); +} + static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject, jint scalingMode) { auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); @@ -824,6 +844,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplayPowerMode }, {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V", (void*)nativeDeferTransactionUntil }, + {"nativeDeferTransactionUntilSurface", "(JJJ)V", + (void*)nativeDeferTransactionUntilSurface }, + {"nativeReparentChildren", "(JLandroid/os/IBinder;)V", + (void*)nativeReparentChildren } , + {"nativeSeverChildren", "(J)V", + (void*)nativeSeverChildren } , {"nativeSetOverrideScalingMode", "(JI)V", (void*)nativeSetOverrideScalingMode }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp index dad6958560c0..508d89795569 100644 --- a/core/jni/android_view_SurfaceSession.cpp +++ b/core/jni/android_view_SurfaceSession.cpp @@ -24,6 +24,7 @@ #include <utils/RefBase.h> #include <gui/SurfaceComposerClient.h> +#include <gui/Surface.h> namespace android { @@ -45,6 +46,13 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz) { return reinterpret_cast<jlong>(client); } +static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) { + Surface *parent = reinterpret_cast<Surface*>(surfaceObject); + SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer()); + client->incStrong((void*)nativeCreate); + return reinterpret_cast<jlong>(client); +} + static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr); client->decStrong((void*)nativeCreate); @@ -55,11 +63,12 @@ static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) { client->dispose(); } - static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeCreate", "()J", (void*)nativeCreate }, + { "nativeCreateScoped", "(J)J", + (void*)nativeCreateScoped }, { "nativeDestroy", "(J)V", (void*)nativeDestroy }, { "nativeKill", "(J)V", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 054fad2f0bca..aea7a5d9beec 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -522,6 +522,8 @@ <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.content.pm.action.SESSION_COMMITTED" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -895,6 +897,17 @@ android:description="@string/permdesc_processOutgoingCalls" android:protectionLevel="dangerous" /> + + <!-- Allows the app to answer an incoming phone call. + <p>Protection level: dangerous + --> + <permission android:name="android.permission.ANSWER_PHONE_CALLS" + android:permissionGroup="android.permission-group.PHONE" + android:label="@string/permlab_answerPhoneCalls" + android:description="@string/permdesc_answerPhoneCalls" + android:protectionLevel="dangerous" /> + + <!-- ====================================================================== --> <!-- Permissions for accessing the device microphone --> <!-- ====================================================================== --> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index be7760398062..e8169f853121 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3148,19 +3148,19 @@ If both layout_marginHorizontal and any of layout_marginLeft, layout_marginRight, layout_marginStart, and layout_marginEnd are also specified, the layout_marginHorizontal value will take precedence over the - edge-specific values. Also, layout_margin will always take precendent over + edge-specific values. Also, layout_margin will always take precedence over any of these values, including layout_marginHorizontal. This space is outside this view's bounds. Margin values should be positive.--> <attr name="layout_marginHorizontal" format="dimension" /> - <!-- Specifies extra space on the tyop and bottom sides of this view. + <!-- Specifies extra space on the top and bottom sides of this view. Specifying layout_marginVertical is equivalent to specifying layout_marginTop and layout_marginBottom with that same value. If both layout_marginVertical and either/both layout_marginTop and layout_marginBottom are also specified, the layout_marginVertical value will take precedence over the edge-specific values. - Also, layout_margin will always take precendent over - any of these values, including layout_marginHorizontal. + Also, layout_margin will always take precedence over + any of these values, including layout_marginVertical. This space is outside this view's bounds. Margin values should be positive.--> <attr name="layout_marginVertical" format="dimension" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b0c532ca72a9..ea0666413f88 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -587,6 +587,48 @@ <!-- Text shown in place of notification contents when the notification is hidden by policy on a secure lockscreen --> <string name="notification_hidden_by_policy_text">Contents hidden by policy</string> + <!-- Text shown when viewing channel settings for notifications related to the virtual keyboard --> + <string name="notification_channel_virtual_keyboard">Virtual keyboard</string> + + <!-- Text shown when viewing channel settings for notifications related to the hardware keyboard --> + <string name="notification_channel_physical_keyboard">Physical keyboard</string> + + <!-- Text shown when viewing channel settings for notifications related to security --> + <string name="notification_channel_security">Security</string> + + <!-- Text shown when viewing channel settings for notifications related to car mode --> + <string name="notification_channel_car_mode">Car mode</string> + + <!-- Text shown when viewing channel settings for notifications related to account status --> + <string name="notification_channel_account">Account status</string> + + <!-- Text shown when viewing channel settings for notifications related to developers --> + <string name="notification_channel_developer">Developer messages</string> + + <!-- Text shown when viewing channel settings for notifications related to system updates --> + <string name="notification_channel_updates">Updates</string> + + <!-- Text shown when viewing channel settings for notifications related to network status --> + <string name="notification_channel_network_status">Network status</string> + + <!-- Text shown when viewing channel settings for notifications related to network alerts --> + <string name="notification_channel_network_alerts">Network alerts</string> + + <!-- Text shown when viewing channel settings for notifications related to vpn status --> + <string name="notification_channel_vpn">VPN status</string> + + <!-- Text shown when viewing channel settings for notifications related to remote device administration --> + <string name="notification_channel_device_admin">Device administration</string> + + <!-- Text shown when viewing channel settings for notifications related to important alerts --> + <string name="notification_channel_alerts">Alerts</string> + + <!-- Text shown when viewing channel settings for notifications related to being in retail mode --> + <string name="notification_channel_retail_mode">Retail demo</string> + + <!-- Text shown when viewing channel settings for notifications related to a usb connection --> + <string name="notification_channel_usb">USB connection</string> + <!-- Displayed to the user to tell them that they have started up the phone in "safe mode" --> <string name="safeMode">Safe mode</string> @@ -728,6 +770,11 @@ the call to a different number or abort the call altogether.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_answerPhoneCalls">answer phone calls</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_answerPhoneCalls">Allows the app to answer an incoming phone call.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_receiveSms">receive text messages (SMS)</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_receiveSms">Allows the app to receive and process SMS diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 15abf74ef38c..3ba6a274ee8a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2887,4 +2887,21 @@ <!-- Colon separated list of package names that should be granted Notification Listener access --> <java-symbol type="string" name="config_defaultListenerAccessPackages" /> + + <!-- system notification channels --> + <java-symbol type="string" name="notification_channel_virtual_keyboard" /> + <java-symbol type="string" name="notification_channel_physical_keyboard" /> + <java-symbol type="string" name="notification_channel_security" /> + <java-symbol type="string" name="notification_channel_car_mode" /> + <java-symbol type="string" name="notification_channel_account" /> + <java-symbol type="string" name="notification_channel_developer" /> + <java-symbol type="string" name="notification_channel_updates" /> + <java-symbol type="string" name="notification_channel_network_status" /> + <java-symbol type="string" name="notification_channel_network_alerts" /> + <java-symbol type="string" name="notification_channel_vpn" /> + <java-symbol type="string" name="notification_channel_device_admin" /> + <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" /> + </resources> diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java index e5a92bf23bdc..5dd3c2ce6b5d 100644 --- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java +++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java @@ -91,6 +91,28 @@ public class ParceledListSliceTest extends TestCase { } } + public void testStringList() throws Exception { + final int objectCount = 400; + List<String> list = new ArrayList<String>(); + for (long i = 0; i < objectCount; i++) { + list.add(Long.toString(i * (6 - i))); + } + + StringParceledListSlice slice; + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(new StringParceledListSlice(list), 0); + parcel.setDataPosition(0); + slice = parcel.readParcelable(getClass().getClassLoader()); + } finally { + parcel.recycle(); + } + + assertNotNull(slice); + assertNotNull(slice.getList()); + assertEquals(list, slice.getList()); + } + /** * Test that only homogeneous elements may be unparceled. */ diff --git a/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java b/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java index 685a9980fdc4..c2dbfd365964 100644 --- a/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java @@ -38,11 +38,11 @@ public class BitmapTest extends TestCase { assertEquals("rowbytes", 400, bm1.getRowBytes()); assertEquals("rowbytes", 200, bm2.getRowBytes()); - assertEquals("rowbytes", 200, bm3.getRowBytes()); - + assertEquals("rowbytes", 400, bm3.getRowBytes()); + assertEquals("byteCount", 80000, bm1.getByteCount()); assertEquals("byteCount", 40000, bm2.getByteCount()); - assertEquals("byteCount", 40000, bm3.getByteCount()); + assertEquals("byteCount", 80000, bm3.getByteCount()); assertEquals("height", 200, bm1.getHeight()); assertEquals("height", 200, bm2.getHeight()); @@ -51,10 +51,10 @@ public class BitmapTest extends TestCase { assertTrue("hasAlpha", bm1.hasAlpha()); assertFalse("hasAlpha", bm2.hasAlpha()); assertTrue("hasAlpha", bm3.hasAlpha()); - + assertTrue("getConfig", bm1.getConfig() == Bitmap.Config.ARGB_8888); assertTrue("getConfig", bm2.getConfig() == Bitmap.Config.RGB_565); - assertTrue("getConfig", bm3.getConfig() == Bitmap.Config.ARGB_4444); + assertTrue("getConfig", bm3.getConfig() == Bitmap.Config.ARGB_8888); } @SmallTest @@ -181,12 +181,12 @@ public class BitmapTest extends TestCase { for (int i = 0; i < 256; i++) { colors[i] = (i << 24) | (0xFF << 16) | (0x80 << 8) | 0; } - + Bitmap.Config config = Bitmap.Config.ARGB_8888; // create a bitmap with the color array specified Bitmap bm1 = Bitmap.createBitmap(colors, 16, 16, config); - + // create a bitmap with no colors, but then call setPixels Bitmap bm2 = Bitmap.createBitmap(16, 16, config); bm2.setPixels(colors, 0, 16, 0, 0, 16, 16); @@ -197,32 +197,32 @@ public class BitmapTest extends TestCase { int c0 = colors[i]; int c1 = bm1.getPixel(i % 16, i / 16); int c2 = bm2.getPixel(i % 16, i / 16); - + // these two should always be identical assertEquals("getPixel", c1, c2); - + // comparing the original (c0) with the returned color is tricky, // since it gets premultiplied during the set(), and unpremultiplied // by the get(). int a0 = Color.alpha(c0); int a1 = Color.alpha(c1); assertEquals("alpha", a0, a1); - + int r0 = Color.red(c0); int r1 = Color.red(c1); int rr = computePrePostMul(a0, r0); assertTrue("red", Math.abs(rr - r1) <= tolerance); - + int g0 = Color.green(c0); int g1 = Color.green(c1); int gg = computePrePostMul(a0, g0); assertTrue("green", Math.abs(gg - g1) <= tolerance); - + int b0 = Color.blue(c0); int b1 = Color.blue(c1); int bb = computePrePostMul(a0, b0); assertTrue("blue", Math.abs(bb - b1) <= tolerance); - + if (false) { int cc = Color.argb(a0, rr, gg, bb); android.util.Log.d("skia", "original " + Integer.toHexString(c0) + @@ -231,4 +231,16 @@ public class BitmapTest extends TestCase { } } } + + @SmallTest + public void testCreateHardwareBitmapFromGraphicBuffer() { + GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK); + Canvas canvas = buffer.lockCanvas(); + canvas.drawColor(Color.YELLOW); + buffer.unlockCanvasAndPost(canvas); + Bitmap hardwareBitmap = Bitmap.createHardwareBitmap(buffer); + assertTrue(hardwareBitmap.isPremultiplied()); + assertFalse(hardwareBitmap.isMutable()); + } } diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index deea9726403f..b68543146a51 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -15,7 +15,7 @@ */ package android.security; -import android.content.pm.ParceledListSlice; +import android.content.pm.StringParceledListSlice; /** * Caller is required to ensure that {@link KeyStore#unlock @@ -39,8 +39,8 @@ interface IKeyChainService { // APIs used by Settings boolean deleteCaCertificate(String alias); boolean reset(); - ParceledListSlice getUserCaAliases(); - ParceledListSlice getSystemCaAliases(); + StringParceledListSlice getUserCaAliases(); + StringParceledListSlice getSystemCaAliases(); boolean containsCaAlias(String alias); byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem); List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 7cab11a4cf81..c57b1b38bf7b 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -73,6 +73,24 @@ void SkiaCanvas::reset(SkCanvas* skiaCanvas) { // Canvas state operations: Replace Bitmap // ---------------------------------------------------------------------------- +class ClipCopier : public SkCanvas::ClipVisitor { +public: + explicit ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + + virtual void clipRect(const SkRect& rect, SkClipOp op, bool antialias) { + m_dstCanvas->clipRect(rect, op, antialias); + } + virtual void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) { + m_dstCanvas->clipRRect(rrect, op, antialias); + } + virtual void clipPath(const SkPath& path, SkClipOp op, bool antialias) { + m_dstCanvas->clipPath(path, op, antialias); + } + +private: + SkCanvas* m_dstCanvas; +}; + void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { SkCanvas* newCanvas = new SkCanvas(bitmap); @@ -80,9 +98,8 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { // Copy the canvas matrix & clip state. newCanvas->setMatrix(mCanvas->getTotalMatrix()); - SkRegion rgn; - mCanvas->temporary_internal_getRgnClip(&rgn); - newCanvas->clipRegion(rgn, SkClipOp::kIntersect); + ClipCopier copier(newCanvas); + mCanvas->replayClips(&copier); } // deletes the previously owned canvas (if any) diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1ebbe855a43f..fb37f9f62d14 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1251,6 +1251,11 @@ public class MediaPlayer extends PlayerBase public void prepare() throws IOException, IllegalStateException { _prepare(); scanInternalSubtitleTracks(); + + // DrmInfo, if any, has been resolved by now. + synchronized (mDrmLock) { + mDrmInfoResolved = true; + } } private native void _prepare() throws IOException, IllegalStateException; @@ -3149,12 +3154,6 @@ public class MediaPlayer extends PlayerBase sendMessage(msg2); } - // MEDIA_DRM_INFO is fired (if available) before MEDIA_PREPARED. - // An empty mDrmInfo indicates prepared is done but the source is not DRM protected. - // Setting this before the callback so onPreparedListener can call getDrmInfo to - // get the right state - mDrmInfoResolved = true; - OnPreparedListener onPreparedListener = mOnPreparedListener; if (onPreparedListener != null) onPreparedListener.onPrepared(mMediaPlayer); @@ -3166,12 +3165,14 @@ public class MediaPlayer extends PlayerBase if (msg.obj == null) { Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL"); } else if (msg.obj instanceof Parcel) { - Parcel parcel = (Parcel)msg.obj; - DrmInfo drmInfo = new DrmInfo(parcel); + // The parcel was parsed already in postEventFromNative + DrmInfo drmInfo = null; OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate; synchronized (mDrmLock) { - mDrmInfo = drmInfo.makeCopy(); + if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) { + drmInfo = mDrmInfo.makeCopy(); + } // local copy while keeping the lock onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate; } @@ -3366,10 +3367,43 @@ public class MediaPlayer extends PlayerBase return; } - if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) { - // this acquires the wakelock if needed, and sets the client side state - mp.start(); + switch (what) { + case MEDIA_INFO: + if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) { + // this acquires the wakelock if needed, and sets the client side state + mp.start(); + } + break; + + case MEDIA_DRM_INFO: + // We need to derive mDrmInfo before prepare() returns so processing it here + // before the notification is sent to EventHandler below. EventHandler runs in the + // notification looper so its handleMessage might process the event after prepare() + // has returned. + Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO"); + if (obj instanceof Parcel) { + Parcel parcel = (Parcel)obj; + DrmInfo drmInfo = new DrmInfo(parcel); + synchronized (mp.mDrmLock) { + mp.mDrmInfo = drmInfo; + } + } else { + Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj); + } + break; + + case MEDIA_PREPARED: + // By this time, we've learned about DrmInfo's presence or absence. This is meant + // mainly for prepareAsync() use case. For prepare(), this still can run to a race + // condition b/c MediaPlayerNative releases the prepare() lock before calling notify + // so we also set mDrmInfoResolved in prepare(). + synchronized (mp.mDrmLock) { + mp.mDrmInfoResolved = true; + } + break; + } + if (mp.mEventHandler != null) { Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); mp.mEventHandler.sendMessage(m); @@ -4093,16 +4127,16 @@ public class MediaPlayer extends PlayerBase * If the device has not been provisioned before, this call also provisions the device * which involves accessing the provisioning server and can take a variable time to * complete depending on the network connectivity. - * If OnDrmPreparedListener is registered, prepareDrm() runs in non-blocking + * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking * mode by launching the provisioning in the background and returning. The listener * will be called when provisioning and preperation has finished. If a - * OnDrmPreparedListener is not registered, prepareDrm() waits till provisioning + * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning * and preperation has finished, i.e., runs in blocking mode. * <p> - * If OnDrmPreparedListener is registered, it is called to indicated the DRM session - * being ready regardless of blocking or non-blocking mode. The application should - * not make any assumption about its call sequence (e.g., before or after prepareDrm - * returns) or the thread context that will execute the listener. + * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM + * session being ready. The application should not make any assumption about its call + * sequence (e.g., before or after prepareDrm returns), or the thread context that will + * execute the listener (unless the listener is registered with a handler thread). * <p> * * @param uuid The UUID of the crypto scheme. diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 1b53d72d6e55..6e15d8da0033 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -70,9 +70,79 @@ public final class TvContract { private static final String PATH_PROGRAM = "program"; private static final String PATH_RECORDED_PROGRAM = "recorded_program"; private static final String PATH_PREVIEW_PROGRAM = "preview_program"; + private static final String PATH_WATCH_NEXT_PROGRAM = "watch_next_program"; private static final String PATH_PASSTHROUGH = "passthrough"; /** + * Activity Action: sent by an application telling the system to set the given channel as + * browsable. This is only relevant to channels with {@link Channels#TYPE_PREVIEW} type. + * + * <p>The intent must contain the following bundle parameters: + * <ul> + * <li>{@link #EXTRA_CHANNEL_ID}: ID for the {@link Channels#TYPE_PREVIEW} channel as a long + * integer.</li> + * <li>{@link #EXTRA_PACKAGE_NAME}: the package name of the requesting application.</li> + * </ul> + */ + public static final String ACTION_MAKE_CHANNEL_BROWSABLE = + "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; + + /** + * Broadcast Action: sent by the system to tell the target TV input that one of its preview + * program's browsable state is disabled, i.e., it will no longer be shown to users, which, for + * example, might be a result of users' interaction with UI. The input is expected to delete the + * preview program from the content provider. + * + * <p>The intent must contain the following bundle parameter: + * <ul> + * <li>{@link #EXTRA_PREVIEW_PROGRAM_ID}: the disabled preview program ID.</li> + * </ul> + */ + public static final String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = + "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED"; + + /** + * Broadcast Action: sent by the system to tell the target TV input that one of its "watch next" + * program's browsable state is disabled, i.e., it will no longer be shown to users, which, for + * example, might be a result of users' interaction with UI. The input is expected to delete the + * "watch next" program from the content provider. + * + * <p>The intent must contain the following bundle parameter: + * <ul> + * <li>{@link #EXTRA_WATCH_NEXT_PROGRAM_ID}: the disabled "watch next" program ID.</li> + * </ul> + */ + public static final String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = + "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED"; + + /** + * Broadcast Action: sent by the system to tell the target TV input that one of its existing + * preview programs is added to the watch next programs table by user. + * + * <p>The intent must contain the following bundle parameters: + * <ul> + * <li>{@link #EXTRA_PREVIEW_PROGRAM_ID}: the ID of the existing preview program.</li> + * <li>{@link #EXTRA_WATCH_NEXT_PROGRAM_ID}: the ID of the new watch next program.</li> + * </ul> + */ + public static final String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = + "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT"; + + /** The key for a bundle parameter containing a channel ID as a long integer */ + public static final String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; + + /** The key for a bundle parameter containing a package name as a string. */ + public static final String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; + + /** The key for a bundle parameter containing a program ID as a long integer. */ + public static final String EXTRA_PREVIEW_PROGRAM_ID = + "android.media.tv.extra.PREVIEW_PROGRAM_ID"; + + /** The key for a bundle parameter containing a watch next program ID as a long integer. */ + public static final String EXTRA_WATCH_NEXT_PROGRAM_ID = + "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; + + /** * The method name to get existing columns in the given table of the specified content provider. * * <p>The method caller must provide the following parameter: @@ -421,6 +491,15 @@ public final class TvContract { } /** + * Builds a URI that points to a specific watch next program. + * + * @param watchNextProgramId The ID of the watch next program to point to. + */ + public static final Uri buildWatchNextProgramUri(long watchNextProgramId) { + return ContentUris.withAppendedId(WatchNextPrograms.CONTENT_URI, watchNextProgramId); + } + + /** * Builds a URI that points to a specific program the user watched. * * @param watchedProgramId The ID of the watched program to point to. @@ -492,17 +571,6 @@ public final class TvContract { */ public interface BaseProgramColumns extends BaseTvColumns { /** - * The ID of the TV channel that provides this TV program. - * - * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}. - * - * <p>This is a required field. - * - * <p>Type: INTEGER (long) - */ - public static final String COLUMN_CHANNEL_ID = "channel_id"; - - /** * The title of this TV program. * * <p>If this program is an episodic TV show, it is recommended that the title is the series @@ -766,6 +834,591 @@ public final class TvContract { public static final String COLUMN_VERSION_NUMBER = "version_number"; } + /** + * Common base for the tables of preview programs. + */ + public interface BasePreviewProgramColumns extends BaseProgramColumns { + + /** @hide */ + @StringDef({ + TYPE_MOVIE, + TYPE_TV_SERIES, + TYPE_TV_SEASON, + TYPE_TV_EPISODE, + TYPE_CLIP, + TYPE_EVENT, + TYPE_CHANNEL, + TYPE_TRACK, + TYPE_ALBUM, + TYPE_ARTIST, + TYPE_PLAYLIST, + TYPE_STATION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** + * The program type for movie. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_MOVIE = "TYPE_MOVIE"; + + /** + * The program type for TV series. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + + /** + * The program type for TV season. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + + /** + * The program type for TV episode. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + + /** + * The program type for clip. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_CLIP = "TYPE_CLIP"; + + /** + * The program type for event. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_EVENT = "TYPE_EVENT"; + + /** + * The program type for channel. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_CHANNEL = "TYPE_CHANNEL"; + + /** + * The program type for track. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_TRACK = "TYPE_TRACK"; + + /** + * The program type for album. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_ALBUM = "TYPE_ALBUM"; + + /** + * The program type for artist. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_ARTIST = "TYPE_ARTIST"; + + /** + * The program type for playlist. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + + /** + * The program type for station. + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_STATION = "TYPE_STATION"; + + /** @hide */ + @StringDef({ + ASPECT_RATIO_16_9, + ASPECT_RATIO_3_2, + ASPECT_RATIO_1_1, + ASPECT_RATIO_2_3, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AspectRatio {} + + /** + * The aspect ratio for 16:9. + * + * @see #COLUMN_POSTER_ART_ASPECT_RATIO + * @see #COLUMN_THUMBNAIL_ASPECT_RATIO + */ + public static final String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + + /** + * The aspect ratio for 3:2. + * + * @see #COLUMN_POSTER_ART_ASPECT_RATIO + * @see #COLUMN_THUMBNAIL_ASPECT_RATIO + */ + public static final String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + + /** + * The aspect ratio for 1:1. + * + * @see #COLUMN_POSTER_ART_ASPECT_RATIO + * @see #COLUMN_THUMBNAIL_ASPECT_RATIO + */ + public static final String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + + /** + * The aspect ratio for 2:3. + * + * @see #COLUMN_POSTER_ART_ASPECT_RATIO + * @see #COLUMN_THUMBNAIL_ASPECT_RATIO + */ + public static final String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + + /** @hide */ + @StringDef({ + AVAILABILITY_AVAILABLE, + AVAILABILITY_FREE_WITH_SUBSCRIPTION, + AVAILABILITY_PAID_CONTENT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Availability {} + + /** + * The availability for "available to this user". + * + * @see #COLUMN_AVAILABILITY + */ + public static final String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + + /** + * The availability for "free with subscription". + * + * @see #COLUMN_AVAILABILITY + */ + public static final String AVAILABILITY_FREE_WITH_SUBSCRIPTION = + "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + + /** + * The availability for "paid content, either to-own or rental + * (user has not purchased/rented). + * + * @see #COLUMN_AVAILABILITY + */ + public static final String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + + /** @hide */ + @StringDef({ + INTERACTION_TYPE_LISTENS, + INTERACTION_TYPE_FOLLOWERS, + INTERACTION_TYPE_FANS, + INTERACTION_TYPE_LIKES, + INTERACTION_TYPE_THUMBS, + INTERACTION_TYPE_VIEWS, + INTERACTION_TYPE_VIEWERS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface InteractionType {} + + /** + * The interaction type for "listens". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + + /** + * The interaction type for "followers". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + + /** + * The interaction type for "fans". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + + /** + * The interaction type for "likes". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + + /** + * The interaction type for "thumbs". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + + /** + * The interaction type for "views". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + + /** + * The interaction type for "viewers". + * + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + + /** @hide */ + @StringDef({ + REVIEW_RATING_STYLE_STARS, + REVIEW_RATING_STYLE_THUMBS_UP_DOWN, + REVIEW_RATING_STYLE_PERCENTAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ReviewRatingStyle {} + + /** + * The review rating style for five star rating. + * + * @see #COLUMN_REVIEW_RATING_STYLE + */ + public static final String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + + /** + * The review rating style for thumbs-up and thumbs-down rating. + * + * @see #COLUMN_REVIEW_RATING_STYLE + */ + public static final String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = + "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + + /** + * The review rating style for 0 to 100 point system. + * + * @see #COLUMN_REVIEW_RATING_STYLE + */ + public static final String REVIEW_RATING_STYLE_PERCENTAGE = + "REVIEW_RATING_STYLE_PERCENTAGE"; + + /** + * The type of this program content. + * + * <p>The value should match one of the followings: + * {@link #TYPE_MOVIE}, + * {@link #TYPE_TV_SERIES}, + * {@link #TYPE_TV_SEASON}, + * {@link #TYPE_TV_EPISODE}, + * {@link #TYPE_CLIP}, + * {@link #TYPE_EVENT}, + * {@link #TYPE_CHANNEL}, + * {@link #TYPE_TRACK}, + * {@link #TYPE_ALBUM}, + * {@link #TYPE_ARTIST}, + * {@link #TYPE_PLAYLIST}, and + * {@link #TYPE_STATION}. + * + * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW} + * channel. + * + * <p>Type: TEXT + */ + public static final String COLUMN_TYPE = "type"; + + /** + * The aspect ratio of the poster art for this TV program. + * + * <p>The value should match one of the followings: + * {@link #ASPECT_RATIO_16_9}, + * {@link #ASPECT_RATIO_3_2}, + * {@link #ASPECT_RATIO_1_1}, and + * {@link #ASPECT_RATIO_2_3}. + * + * <p>Type: TEXT + */ + public static final String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + + /** + * The aspect ratio of the thumbnail for this TV program. + * + * <p>The value should match one of the followings: + * {@link #ASPECT_RATIO_16_9}, + * {@link #ASPECT_RATIO_3_2}, + * {@link #ASPECT_RATIO_1_1}, and + * {@link #ASPECT_RATIO_2_3}. + * + * <p>Type: TEXT + */ + public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + + /** + * The URI for the logo of this TV program. + * + * <p>This is a small badge shown on top of the poster art or thumbnail representing the + * source of the content. + * + * <p>The data in the column must be a URL, or a URI in one of the following formats: + * + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * </li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * + * <p>Can be empty. + * + * <p>Type: TEXT + */ + public static final String COLUMN_LOGO_URI = "logo_uri"; + + /** + * The availability of this TV program. + * + * <p>The value should match one of the followings: + * {@link #AVAILABILITY_AVAILABLE}, + * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and + * {@link #AVAILABILITY_PAID_CONTENT}. + * + * <p>Type: TEXT + */ + public static final String COLUMN_AVAILABILITY = "availability"; + + /** + * The starting price of this TV program. + * + * <p>This indicates the lowest regular acquisition cost of the content. It is only used + * if the availability of the program is {@link #AVAILABILITY_PAID_CONTENT}. + * + * <p>Type: TEXT + * @see #COLUMN_OFFER_PRICE + */ + public static final String COLUMN_STARTING_PRICE = "starting_price"; + + /** + * The offer price of this TV program. + * + * <p>This is the promotional cost of the content. It is only used if the availability of + * the program is {@link #AVAILABILITY_PAID_CONTENT}. + * + * <p>Type: TEXT + * @see #COLUMN_STARTING_PRICE + */ + public static final String COLUMN_OFFER_PRICE = "offer_price"; + + /** + * The release date of this TV program. + * + * <p>The value should be in the form of either "yyyy-MM-dd" or "yyyy". + * + * <p>Type: TEXT + */ + public static final String COLUMN_RELEASE_DATE = "release_date"; + + /** + * The count of the items included in this TV program. + * + * <p>This is only relevant if the program represents a collection of items such as series, + * episodes, or music tracks. + * + * <p>Type: INTEGER + */ + public static final String COLUMN_ITEM_COUNT = "item_count"; + + /** + * The flag indicating whether this TV program is live or not. + * + * <p>A value of 1 indicates that the content is airing and should be consumed now, a value + * of 0 indicates that the content is off the air and does not need to be consumed at the + * present time. If not specified, the value is set to 0 (not live) by default. + * + * <p>Type: INTEGER (boolean) + */ + public static final String COLUMN_LIVE = "live"; + + /** + * The internal ID used by individual TV input services. + * + * <p>This is internal to the provider that inserted it, and should not be decoded by other + * apps. + * + * <p>Can be empty. + * + * <p>Type: TEXT + */ + public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + + /** + * The URI for the preview video. + * + * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. The data in the column must be + * a URL, or a URI in one of the following formats: + * + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * </li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * + * <p>Can be empty. + * + * <p>Type: TEXT + */ + public static final String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + + /** + * The last playback position (in milliseconds) of the preview video. + * + * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. + * + * <p>Can be empty. + * + * <p>Type: INTEGER + */ + public static final String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = + "last_playback_position_millis"; + + /** + * The duration (in milliseconds) of the preview video. + * + * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. + * + * <p>Can be empty. + * + * <p>Type: INTEGER + */ + public static final String COLUMN_DURATION_MILLIS = "duration_millis"; + + /** + * The intent URI which is launched when the preview video is selected. + * + * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME} + * and converted back to the original intent with {@link Intent#parseUri}. The intent is + * launched when the user selects the preview video item. + * + * <p>Can be empty. + * + * <p>Type: TEXT + */ + public static final String COLUMN_APP_LINK_INTENT_URI = + "app_link_intent_uri"; + + /** + * The flag indicating whether this program is transient or not. + * + * <p>A value of 1 indicates that the channel will be automatically removed by the system on + * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not + * specified, this value is set to 0 (not transient) by default. + * + * <p>Type: INTEGER (boolean) + * @see Channels#COLUMN_TRANSIENT + * @hide + */ + @SystemApi + public static final String COLUMN_TRANSIENT = "transient"; + + /** + * The type of interaction for this TV program. + * + * <p> The value should match one of the followings: + * {@link #INTERACTION_TYPE_LISTENS}, + * {@link #INTERACTION_TYPE_FOLLOWERS}, + * {@link #INTERACTION_TYPE_FANS}, + * {@link #INTERACTION_TYPE_LIKES}, + * {@link #INTERACTION_TYPE_THUMBS}, + * {@link #INTERACTION_TYPE_VIEWS}, and + * {@link #INTERACTION_TYPE_VIEWERS}. + * + * <p>Type: TEXT + * @see #COLUMN_INTERACTION_COUNT + */ + public static final String COLUMN_INTERACTION_TYPE = "interaction_type"; + + /** + * The interaction count for this program. + * + * <p>This indicates the number of times interaction has happened. + * + * <p>Type: INTEGER (long) + * @see #COLUMN_INTERACTION_TYPE + */ + public static final String COLUMN_INTERACTION_COUNT = "interaction_count"; + + /** + * The author or artist of this content. + * + * <p>Type: TEXT + */ + public static final String COLUMN_AUTHOR = "author"; + + /** + * The review rating score style used for {@link #COLUMN_REVIEW_RATING}. + * + * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS}, + * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}. + * + * <p>Type: TEXT + * @see #COLUMN_REVIEW_RATING + */ + public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + + /** + * The review rating score for this program. + * + * <p>The format of the value is dependent on {@link #COLUMN_REVIEW_RATING_STYLE}. If the + * style is {@link #REVIEW_RATING_STYLE_STARS}, the value should be a real number between + * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, + * the value should be two integers, one for thumbs-up count and the other for thumbs-down + * count, with a comma between them. (e.g. "200,40") If the style is + * {@link #REVIEW_RATING_STYLE_PERCENTAGE}, the value shoule be a real number between 0 and + * 100. (e.g. "99.9") + * + * <p>Type: TEXT + * @see #COLUMN_REVIEW_RATING_STYLE + */ + public static final String COLUMN_REVIEW_RATING = "review_rating"; + + /** + * The flag indicating whether this TV program is browsable or not. + * + * <p>This column can only be set by applications having proper system permission. For + * other applications, this is a read-only column. + * + * <p>A value of 1 indicates that the program is browsable and can be shown to users in + * the UI. A value of 0 indicates that the program should be hidden from users and the + * application who changes this value to 0 should send + * {@link #ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED} to the owner of the program + * to notify this change. + * + * <p>This value is set to 1 (browsable) by default. + * + * <p>Type: INTEGER (boolean) + */ + public static final String COLUMN_BROWSABLE = "browsable"; + + /** + * The content ID of this TV program. + * + * <p>A public ID of the content which allows the application to apply the same operation to + * all the program copies in different channels. + * + * <p>Can be empty. + * + * <p>Type: TEXT + */ + public static final String COLUMN_CONTENT_ID = "content_id"; + + } + /** Column definitions for the TV channels table. */ public static final class Channels implements BaseTvColumns { @@ -1279,14 +1932,15 @@ public final class TvContract { /** * The flag indicating whether this TV channel is browsable or not. * + * <p>This column can only be set by applications having proper system permission. For + * other applications, this is a read-only column. + * * <p>A value of 1 indicates the channel is included in the channel list that applications * use to browse channels, a value of 0 indicates the channel is not included in the list. * If not specified, this value is set to 0 (not browsable) by default. * * <p>Type: INTEGER (boolean) - * @hide */ - @SystemApi public static final String COLUMN_BROWSABLE = "browsable"; /** @@ -1587,6 +2241,17 @@ public final class TvContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; /** + * The ID of the TV channel that provides this TV program. + * + * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}. + * + * <p>This is a required field. + * + * <p>Type: INTEGER (long) + */ + public static final String COLUMN_CHANNEL_ID = "channel_id"; + + /** * The season number of this TV program for episodic TV shows. * * <p>Can be empty. @@ -1890,6 +2555,17 @@ public final class TvContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program"; /** + * The ID of the TV channel that provides this recorded program. + * + * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}. + * + * <p>This is a required field. + * + * <p>Type: INTEGER (long) + */ + public static final String COLUMN_CHANNEL_ID = "channel_id"; + + /** * The ID of the TV input service that is associated with this recorded program. * * <p>Use {@link #buildInputId} to build the ID. @@ -1988,7 +2664,7 @@ public final class TvContract { /** * Column definitions for the preview TV programs table. */ - public static final class PreviewPrograms implements BaseProgramColumns { + public static final class PreviewPrograms implements BasePreviewProgramColumns { /** The content:// style URI for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" @@ -2000,631 +2676,132 @@ public final class TvContract { /** The MIME type of a single preview TV program. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program"; - /** @hide */ - @StringDef({ - TYPE_MOVIE, - TYPE_TV_SERIES, - TYPE_TV_SEASON, - TYPE_TV_EPISODE, - TYPE_CLIP, - TYPE_EVENT, - TYPE_CHANNEL, - TYPE_TRACK, - TYPE_ALBUM, - TYPE_ARTIST, - TYPE_PLAYLIST, - TYPE_STATION, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - /** - * The program type for movie. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_MOVIE = "TYPE_MOVIE"; - /** - * The program type for TV series. + * The ID of the TV channel that provides this TV program. * - * @see #COLUMN_TYPE - */ - public static final String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - - /** - * The program type for TV season. + * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}. * - * @see #COLUMN_TYPE - */ - public static final String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - - /** - * The program type for TV episode. + * <p>This is a required field. * - * @see #COLUMN_TYPE + * <p>Type: INTEGER (long) */ - public static final String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + public static final String COLUMN_CHANNEL_ID = "channel_id"; /** - * The program type for clip. + * The weight of the preview program within the channel. * - * @see #COLUMN_TYPE - */ - public static final String TYPE_CLIP = "TYPE_CLIP"; - - /** - * The program type for event. + * <p>The UI may choose to show this item in a different position in the channel row. + * A larger weight value means the program is more important than other programs having + * smaller weight values. The value is relevant for the preview programs in the same + * channel. This is only relevant to {@link Channels#TYPE_PREVIEW}. * - * @see #COLUMN_TYPE - */ - public static final String TYPE_EVENT = "TYPE_EVENT"; - - /** - * The program type for channel. + * <p>Can be empty. * - * @see #COLUMN_TYPE + * <p>Type: INTEGER */ - public static final String TYPE_CHANNEL = "TYPE_CHANNEL"; + public static final String COLUMN_WEIGHT = "weight"; - /** - * The program type for track. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_TRACK = "TYPE_TRACK"; + private PreviewPrograms() {} + } - /** - * The program type for album. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_ALBUM = "TYPE_ALBUM"; + /** + * Column definitions for the "watch next" TV programs table. + */ + public static final class WatchNextPrograms implements BasePreviewProgramColumns { - /** - * The program type for artist. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_ARTIST = "TYPE_ARTIST"; + /** The content:// style URI for this table. */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + + PATH_WATCH_NEXT_PROGRAM); - /** - * The program type for playlist. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + /** The MIME type of a directory of "watch next" TV programs. */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; - /** - * The program type for station. - * - * @see #COLUMN_TYPE - */ - public static final String TYPE_STATION = "TYPE_STATION"; + /** The MIME type of a single preview TV program. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; /** @hide */ @StringDef({ WATCH_NEXT_TYPE_CONTINUE, WATCH_NEXT_TYPE_NEXT, WATCH_NEXT_TYPE_NEW, + WATCH_NEXT_TYPE_WATCHLIST, }) @Retention(RetentionPolicy.SOURCE) public @interface WatchNextType {} /** - * The watch next type for CONTINUE. + * The watch next type for CONTINUE. Use this type when the user has already watched more + * than 1 minute of this content. * * @see #COLUMN_WATCH_NEXT_TYPE */ public static final String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; /** - * The watch next type for NEXT. + * The watch next type for NEXT. Use this type when the user has watched one or more + * complete episodes from some episodic content, but there remains more than one episode + * remaining or there is one last episode remaining, but it is not “new” in that it was + * released before the user started watching the show. * * @see #COLUMN_WATCH_NEXT_TYPE */ public static final String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; /** - * The watch next type for NEW. + * The watch next type for NEW. Use this type when the user had watched all of the available + * episodes from some episodic content, but a new episode became available since the user + * started watching the first episode and now there is exactly one unwatched episode. This + * could also work for recorded events in a series e.g. soccer matches or football games. * * @see #COLUMN_WATCH_NEXT_TYPE */ public static final String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - /** @hide */ - @StringDef({ - ASPECT_RATIO_16_9, - ASPECT_RATIO_3_2, - ASPECT_RATIO_1_1, - ASPECT_RATIO_2_3, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AspectRatio {} - - /** - * The aspect ratio for 16:9. - * - * @see #COLUMN_POSTER_ART_ASPECT_RATIO - * @see #COLUMN_THUMBNAIL_ASPECT_RATIO - */ - public static final String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - - /** - * The aspect ratio for 3:2. - * - * @see #COLUMN_POSTER_ART_ASPECT_RATIO - * @see #COLUMN_THUMBNAIL_ASPECT_RATIO - */ - public static final String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - - /** - * The aspect ratio for 1:1. - * - * @see #COLUMN_POSTER_ART_ASPECT_RATIO - * @see #COLUMN_THUMBNAIL_ASPECT_RATIO - */ - public static final String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - - /** - * The aspect ratio for 2:3. - * - * @see #COLUMN_POSTER_ART_ASPECT_RATIO - * @see #COLUMN_THUMBNAIL_ASPECT_RATIO - */ - public static final String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - - /** @hide */ - @StringDef({ - AVAILABILITY_AVAILABLE, - AVAILABILITY_FREE_WITH_SUBSCRIPTION, - AVAILABILITY_PAID_CONTENT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Availability {} - - /** - * The availability for "available to this user". - * - * @see #COLUMN_AVAILABILITY - */ - public static final String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - - /** - * The availability for "free with subscription". - * - * @see #COLUMN_AVAILABILITY - */ - public static final String AVAILABILITY_FREE_WITH_SUBSCRIPTION = - "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - - /** - * The availability for "paid content, either to-own or rental - * (user has not purchased/rented). - * - * @see #COLUMN_AVAILABILITY - */ - public static final String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - - /** @hide */ - @StringDef({ - INTERACTION_TYPE_LISTENS, - INTERACTION_TYPE_FOLLOWERS, - INTERACTION_TYPE_FANS, - INTERACTION_TYPE_LIKES, - INTERACTION_TYPE_THUMBS, - INTERACTION_TYPE_VIEWS, - INTERACTION_TYPE_VIEWERS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface InteractionType {} - - /** - * The interaction type for "listens". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - - /** - * The interaction type for "followers". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - - /** - * The interaction type for "fans". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - - /** - * The interaction type for "likes". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - - /** - * The interaction type for "thumbs". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - - /** - * The interaction type for "views". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - - /** - * The interaction type for "viewers". - * - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - - /** @hide */ - @StringDef({ - REVIEW_RATING_STYLE_STARS, - REVIEW_RATING_STYLE_THUMBS_UP_DOWN, - REVIEW_RATING_STYLE_PERCENTAGE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ReviewRatingStyle {} - - /** - * The review rating style for five star rating. - * - * @see #COLUMN_REVIEW_RATING_STYLE - */ - public static final String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - - /** - * The review rating style for thumbs-up and thumbs-down rating. - * - * @see #COLUMN_REVIEW_RATING_STYLE - */ - public static final String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = - "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - - /** - * The review rating style for 0 to 100 point system. - * - * @see #COLUMN_REVIEW_RATING_STYLE - */ - public static final String REVIEW_RATING_STYLE_PERCENTAGE = - "REVIEW_RATING_STYLE_PERCENTAGE"; - /** - * The type of this program content. - * - * <p>The value should match one of the followings: - * {@link #TYPE_MOVIE}, - * {@link #TYPE_TV_SERIES}, - * {@link #TYPE_TV_SEASON}, - * {@link #TYPE_TV_EPISODE}, - * {@link #TYPE_CLIP}, - * {@link #TYPE_EVENT}, - * {@link #TYPE_CHANNEL}, - * {@link #TYPE_TRACK}, - * {@link #TYPE_ALBUM}, - * {@link #TYPE_ARTIST}, - * {@link #TYPE_PLAYLIST}, and - * {@link #TYPE_STATION}. + * The watch next type for WATCHLIST. Use this type when the user has elected to explicitly + * add a movie, event or series to a “watchlist” as a manual way of curating what they + * want to watch next. * - * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW} - * channel. - * - * <p>Type: TEXT + * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String COLUMN_TYPE = "type"; + public static final String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; /** * The "watch next" type of this program content. * * <p>The value should match one of the followings: * {@link #WATCH_NEXT_TYPE_CONTINUE}, - * {@link #WATCH_NEXT_TYPE_NEXT}, and - * {@link #WATCH_NEXT_TYPE_NEW}. + * {@link #WATCH_NEXT_TYPE_NEXT}, + * {@link #WATCH_NEXT_TYPE_NEW}, and + * {@link #WATCH_NEXT_TYPE_WATCHLIST}. * - * <p>Can be empty. + * <p>This is a required field. * * <p>Type: TEXT */ public static final String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; /** - * The aspect ratio of the poster art for this TV program. - * - * <p>The value should match one of the followings: - * {@link #ASPECT_RATIO_16_9}, - * {@link #ASPECT_RATIO_3_2}, - * {@link #ASPECT_RATIO_1_1}, and - * {@link #ASPECT_RATIO_2_3}. - * - * <p>Type: TEXT - */ - public static final String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; - - /** - * The aspect ratio of the thumbnail for this TV program. - * - * <p>The value should match one of the followings: - * {@link #ASPECT_RATIO_16_9}, - * {@link #ASPECT_RATIO_3_2}, - * {@link #ASPECT_RATIO_1_1}, and - * {@link #ASPECT_RATIO_2_3}. - * - * <p>Type: TEXT - */ - public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; - - /** - * The URI for the logo of this TV program. - * - * <p>This is a small badge shown on top of the poster art or thumbnail representing the - * source of the content. - * - * <p>The data in the column must be a URL, or a URI in one of the following formats: - * - * <ul> - * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> - * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) - * </li> - * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> - * </ul> - * - * <p>Can be empty. - * - * <p>Type: TEXT - */ - public static final String COLUMN_LOGO_URI = "logo_uri"; - - /** - * The availability of this TV program. - * - * <p>The value should match one of the followings: - * {@link #AVAILABILITY_AVAILABLE}, - * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and - * {@link #AVAILABILITY_PAID_CONTENT}. - * - * <p>Type: TEXT - */ - public static final String COLUMN_AVAILABILITY = "availability"; - - /** - * The starting price of this TV program. - * - * <p>This indicates the lowest regular acquisition cost of the content. It is only used - * if the availability of the program is {@link #AVAILABILITY_PAID_CONTENT}. - * - * <p>Type: TEXT - * @see #COLUMN_OFFER_PRICE - */ - public static final String COLUMN_STARTING_PRICE = "starting_price"; - - /** - * The offer price of this TV program. - * - * <p>This is the promotional cost of the content. It is only used if the availability of - * the program is {@link #AVAILABILITY_PAID_CONTENT}. - * - * <p>Type: TEXT - * @see #COLUMN_STARTING_PRICE - */ - public static final String COLUMN_OFFER_PRICE = "offer_price"; - - /** - * The release date of this TV program. - * - * <p>The value should be in the form of either "yyyy-MM-dd" or "yyyy". - * - * <p>Type: TEXT - */ - public static final String COLUMN_RELEASE_DATE = "release_date"; - - /** - * The count of the items included in this TV program. - * - * <p>This is only relevant if the program represents a collection of items such as series, - * episodes, or music tracks. - * - * <p>Type: INTEGER - */ - public static final String COLUMN_ITEM_COUNT = "item_count"; - - /** - * The flag indicating whether this TV program is live or not. - * - * <p>A value of 1 indicates that the content is airing and should be consumed now, a value - * of 0 indicates that the content is off the air and does not need to be consumed at the - * present time. If not specified, the value is set to 0 (not live) by default. - * - * <p>Type: INTEGER (boolean) - */ - public static final String COLUMN_LIVE = "live"; - - /** - * The internal ID used by individual TV input services. - * - * <p>This is internal to the provider that inserted it, and should not be decoded by other - * apps. - * - * <p>Can be empty. - * - * <p>Type: TEXT - */ - public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; - - /** - * The URI for the preview video. - * - * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. The data in the column must be - * a URL, or a URI in one of the following formats: + * The last UTC time that the user engaged in this TV program, in milliseconds since the + * epoch. This is a hint for the application that is used for ordering of "watch next" + * programs. * + * <p>The meaning of the value varies depending on the {@link #COLUMN_WATCH_NEXT_TYPE}: * <ul> - * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> - * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) - * </li> - * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * <li>{@link #WATCH_NEXT_TYPE_CONTINUE}: the date that the user was last watching the + * content.</li> + * <li>{@link #WATCH_NEXT_TYPE_NEXT}: the date of the last episode watched.</li> + * <li>{@link #WATCH_NEXT_TYPE_NEW}: the release date of the new episode.</li> + * <li>{@link #WATCH_NEXT_TYPE_WATCHLIST}: the date the item was added to the Watchlist. + * </li> * </ul> * - * <p>Can be empty. - * - * <p>Type: TEXT - */ - public static final String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; - - /** - * The last playback position (in milliseconds) of the preview video. - * - * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. - * - * <p>Can be empty. - * - * <p>Type: INTEGER - */ - public static final String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = - "last_playback_position_millis"; - - /** - * The duration (in milliseconds) of the preview video. - * - * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. - * - * <p>Can be empty. - * - * <p>Type: INTEGER - */ - public static final String COLUMN_DURATION_MILLIS = "duration_millis"; - - /** - * The intent URI which is launched when the preview video is selected. - * - * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME} - * and converted back to the original intent with {@link Intent#parseUri}. The intent is - * launched when the user selects the preview video item. - * - * <p>Can be empty. - * - * <p>Type: TEXT - */ - public static final String COLUMN_APP_LINK_INTENT_URI = - "app_link_intent_uri"; - - /** - * The weight of the preview program within the channel. - * - * <p>The UI may choose to show this item in a different position in the channel row. - * A larger weight value means the program is more important than other programs having - * smaller weight values. The value is relevant for the preview programs in the same - * channel. This is only relevant to {@link Channels#TYPE_PREVIEW}. - * - * <p>Can be empty. - * - * <p>Type: INTEGER - */ - public static final String COLUMN_WEIGHT = "weight"; - - /** - * The flag indicating whether this program is transient or not. - * - * <p>A value of 1 indicates that the channel will be automatically removed by the system on - * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not - * specified, this value is set to 0 (not transient) by default. - * - * <p>Type: INTEGER (boolean) - * @see Channels#COLUMN_TRANSIENT - * @hide - */ - @SystemApi - public static final String COLUMN_TRANSIENT = "transient"; - - /** - * The type of interaction for this TV program. - * - * <p> The value should match one of the followings: - * {@link #INTERACTION_TYPE_LISTENS}, - * {@link #INTERACTION_TYPE_FOLLOWERS}, - * {@link #INTERACTION_TYPE_FANS}, - * {@link #INTERACTION_TYPE_LIKES}, - * {@link #INTERACTION_TYPE_THUMBS}, - * {@link #INTERACTION_TYPE_VIEWS}, and - * {@link #INTERACTION_TYPE_VIEWERS}. - * - * <p>Type: TEXT - * @see #COLUMN_INTERACTION_COUNT - */ - public static final String COLUMN_INTERACTION_TYPE = "interaction_type"; - - /** - * The interaction count for this program. - * - * <p>This indicates the number of times interaction has happened. + * <p>This is a required field. * * <p>Type: INTEGER (long) - * @see #COLUMN_INTERACTION_TYPE - */ - public static final String COLUMN_INTERACTION_COUNT = "interaction_count"; - - /** - * The author or artist of this content. - * - * <p>Type: TEXT - */ - public static final String COLUMN_AUTHOR = "author"; - - /** - * The review rating score style used for {@link #COLUMN_REVIEW_RATING}. - * - * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS}, - * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}. - * - * <p>Type: TEXT - * @see #COLUMN_REVIEW_RATING - */ - public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; - - /** - * The review rating score for this program. - * - * <p>The format of the value is dependent on {@link #COLUMN_REVIEW_RATING_STYLE}. If the - * style is {@link #REVIEW_RATING_STYLE_STARS}, the value should be a real number between - * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, - * the value should be two integers, one for thumbs-up count and the other for thumbs-down - * count, with a comma between them. (e.g. "200,40") If the style is - * {@link #REVIEW_RATING_STYLE_PERCENTAGE}, the value shoule be a real number between 0 and - * 100. (e.g. "99.9") - * - * <p>Type: TEXT - * @see #COLUMN_REVIEW_RATING_STYLE */ - public static final String COLUMN_REVIEW_RATING = "review_rating"; - - /** - * The flag indicating whether this TV program is browsable or not. - * - * <p>This column can only be set by system apps. For other applications, it is a read-only - * column. Trying to modify it may cause {@link SecurityException}. - * - * <p>A value of 1 indicates that the program is browsable and can be shown to users in - * the UI. A value of 0 indicates that the program should be hidden from users and the - * application who changes this value to 0 should send - * {@link TvInputManager#ACTION_PROGRAM_BROWSABLE_DISABLED} to the owner of the program - * to notify this change. - * - * <p>This value is set to 1 (browsable) by default. - * - * <p>Type: INTEGER (boolean) - */ - public static final String COLUMN_BROWSABLE = "browsable"; - - private PreviewPrograms() {} + public static final String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = + "last_engagement_time_utc_millis"; } /** diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 4c2b031d8f04..1eae8db60833 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -324,40 +324,6 @@ public final class TvInputManager { public static final String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES"; - /** - * Action sent by the system to tell the target TV input that one of its program's browsable - * state is disabled, i.e., it will no longer be shown to users, which, for example, might - * be a result of users' interaction with UI. - * - * <p>The intent must contain the following bundle parameter: - * <ul> - * <li>{@link #EXTRA_PROGRAM_ID} the program ID as a long integer. - * </ul> - */ - public static final String ACTION_PROGRAM_BROWSABLE_DISABLED = - "android.media.tv.action.PROGRAM_BROWSABLE_DISABLED"; - - /** - * Action sent by an application telling the system to set the given channel as browsable. - * - * <p>The intent must contain the following bundle parameters: - * <ul> - * <li>{@link #EXTRA_CHANNEL_ID} the channel ID as a long integer. - * <li>{@link #EXTRA_PACKAGE_NAME} the package name of the requesting application. - * </ul> - */ - public static final String ACTION_MAKE_CHANNEL_BROWSABLE - = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE"; - - /** The key for a bundle parameter containing a channel ID as a long integer */ - public static final String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID"; - - /** The key for a bundle parameter containing a package name as a string. */ - public static final String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME"; - - /** The key for a bundle parameter containing a program ID as a long integer */ - public static final String EXTRA_PROGRAM_ID = "android.media.tv.extra.PROGRAM_ID"; - private final ITvInputManager mService; private final Object mLock = new Object(); diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index aee9d38e0a27..e5af35711311 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -776,8 +776,8 @@ public class TvView extends ViewGroup { mSurface = null; mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) { @Override - protected void updateWindow() { - super.updateWindow(); + protected void updateSurface() { + super.updateSurface(); relayoutSessionOverlayView(); }}; // The surface view's content should be treated as secure all the time. diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index e2623d4fd9ee..1b1f28c37469 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -170,14 +170,20 @@ LIBANDROID { ASensorEventQueue_getEvents; ASensorEventQueue_hasEvents; ASensorEventQueue_setEventRate; + ASensorManager_configureDirectReport; # introduced=26 ASensorManager_createEventQueue; + ASensorManager_createHardwareBufferDirectChannel; # introduced=26 + ASensorManager_createSharedMemoryDirectChannel; # introduced=26 + ASensorManager_destroyDirectChannel; # introduced=26 ASensorManager_destroyEventQueue; ASensorManager_getDefaultSensor; ASensorManager_getDefaultSensorEx; # introduced=21 ASensorManager_getInstance; + ASensorManager_getInstanceForPackage; # introduced=26 ASensorManager_getSensorList; ASensor_getFifoMaxEventCount; # introduced=21 ASensor_getFifoReservedEventCount; # introduced=21 + ASensor_getHighestDirectReportRateLevel; # introduced=26 ASensor_getMinDelay; ASensor_getName; ASensor_getReportingMode; # introduced=21 @@ -185,6 +191,7 @@ LIBANDROID { ASensor_getStringType; # introduced=21 ASensor_getType; ASensor_getVendor; + ASensor_isDirectChannelTypeSupported; # introduced=26 ASensor_isWakeUpSensor; # introduced=21 ASharedMemory_create; # introduced=26 ASharedMemory_getSize; # introduced=26 diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index 5cfe3004514c..c7bc885ea4b0 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -17,16 +17,17 @@ #define LOG_TAG "sensor" #include <utils/Log.h> +#include <android/hardware_buffer.h> #include <android/looper.h> #include <android/sensor.h> - -#include <utils/RefBase.h> -#include <utils/Looper.h> -#include <utils/Timers.h> - +#include <android/sharedmem.h> +#include <cutils/native_handle.h> #include <gui/Sensor.h> #include <gui/SensorManager.h> #include <gui/SensorEventQueue.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> #include <poll.h> @@ -38,6 +39,22 @@ using android::String8; using android::String16; /*****************************************************************************/ +#define ERROR_INVALID_PARAMETER(message) ALOGE("%s: " message, __func__) + +// frequently used check +#define RETURN_IF_MANAGER_IS_NULL(retval) do {\ + if (manager == nullptr) { \ + ERROR_INVALID_PARAMETER("manager cannot be NULL"); \ + return retval; \ + } \ + } while (false) +#define RETURN_IF_SENSOR_IS_NULL(retval) do {\ + if (sensor == nullptr) { \ + ERROR_INVALID_PARAMETER("sensor cannot be NULL"); \ + return retval; \ + } \ + } while (false) + ASensorManager* ASensorManager_getInstance() { return ASensorManager_getInstanceForPackage(NULL); @@ -103,6 +120,78 @@ int ASensorManager_destroyEventQueue(ASensorManager* manager, return 0; } +int ASensorManager_createSharedMemoryDirectChannel( + ASensorManager *manager, int fd, size_t size) { + RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE); + + if (fd < 0) { + ERROR_INVALID_PARAMETER("fd is invalid."); + return android::BAD_VALUE; + } + + if (size < sizeof(ASensorEvent)) { + ERROR_INVALID_PARAMETER("size has to be greater or equal to sizeof(ASensorEvent)."); + } + + native_handle_t *resourceHandle = native_handle_create(1 /* nFd */, 0 /* nInt */); + if (!resourceHandle) { + return android::NO_MEMORY; + } + + resourceHandle->data[0] = fd; + int ret = static_cast<SensorManager *>(manager)->createDirectChannel( + size, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle); + native_handle_delete(resourceHandle); + return ret; +} + +int ASensorManager_createHardwareBufferDirectChannel( + ASensorManager *manager, AHardwareBuffer const *buffer, size_t size) { + RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE); + + if (buffer == nullptr) { + ERROR_INVALID_PARAMETER("buffer cannot be NULL"); + return android::BAD_VALUE; + } + + if (size < sizeof(ASensorEvent)) { + ERROR_INVALID_PARAMETER("size has to be greater or equal to sizeof(ASensorEvent)."); + } + + const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(buffer); + if (!resourceHandle) { + return android::NO_MEMORY; + } + + return static_cast<SensorManager *>(manager)->createDirectChannel( + size, ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER, resourceHandle); +} + +void ASensorManager_destroyDirectChannel(ASensorManager *manager, int channelId) { + RETURN_IF_MANAGER_IS_NULL(void()); + + static_cast<SensorManager *>(manager)->destroyDirectChannel(channelId); +} + +int ASensorManager_configureDirectReport( + ASensorManager *manager, ASensor const *sensor, int channelId, int rate) { + RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE); + + int sensorHandle; + if (sensor == nullptr) { + if (rate != ASENSOR_DIRECT_RATE_STOP) { + ERROR_INVALID_PARAMETER( + "sensor cannot be null when rate is not ASENSOR_DIRECT_RATE_STOP"); + return android::BAD_VALUE; + } + sensorHandle = -1; + } else { + sensorHandle = static_cast<Sensor const *>(sensor)->getHandle(); + } + return static_cast<SensorManager *>(manager)->configureDirectChannel( + channelId, sensorHandle, rate); +} + /*****************************************************************************/ int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor, @@ -211,3 +300,13 @@ bool ASensor_isWakeUpSensor(ASensor const* sensor) { return static_cast<Sensor const*>(sensor)->isWakeUpSensor(); } + +bool ASensor_isDirectChannelTypeSupported(ASensor const *sensor, int channelType) { + RETURN_IF_SENSOR_IS_NULL(false); + return static_cast<Sensor const *>(sensor)->isDirectChannelTypeSupported(channelType); +} + +int ASensor_getHighestDirectReportRateLevel(ASensor const *sensor) { + RETURN_IF_SENSOR_IS_NULL(ASENSOR_DIRECT_RATE_STOP); + return static_cast<Sensor const *>(sensor)->getHighestDirectReportRateLevel(); +} diff --git a/packages/SettingsLib/res/layout/drawer_category.xml b/packages/SettingsLib/res/layout/drawer_category.xml deleted file mode 100644 index 72cfddb1a431..000000000000 --- a/packages/SettingsLib/res/layout/drawer_category.xml +++ /dev/null @@ -1,38 +0,0 @@ -<!-- - Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:orientation="vertical"> - - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/listDivider" /> - - <TextView - style="@style/TextAppearanceSmall" - android:id="@android:id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="48dp" - android:paddingTop="16dp" - android:paddingBottom="16dp" - android:paddingStart="16dp" /> - -</LinearLayout> diff --git a/packages/SettingsLib/res/layout/drawer_item.xml b/packages/SettingsLib/res/layout/drawer_item.xml deleted file mode 100644 index d492ddfee0fe..000000000000 --- a/packages/SettingsLib/res/layout/drawer_item.xml +++ /dev/null @@ -1,44 +0,0 @@ -<!-- - Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/tile_item" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="48dp" - android:orientation="horizontal" > - - <ImageView - android:id="@android:id/icon" - android:layout_width="@dimen/drawer_icon_size" - android:layout_height="@dimen/drawer_icon_size" - android:layout_marginStart="@dimen/drawer_icon_margin" - android:layout_marginEnd="@dimen/drawer_icon_margin" - android:layout_marginTop="@dimen/drawer_item_top_bottom_margin" - android:layout_marginBottom="@dimen/drawer_item_top_bottom_margin" - android:scaleType="fitCenter" - android:layout_gravity="center_vertical" - android:tint="?android:attr/colorAccent"/> - - <TextView - android:textAppearance="@style/TextAppearanceMedium" - android:id="@android:id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:textColor="?android:attr/colorControlNormal" /> - -</LinearLayout> diff --git a/packages/SettingsLib/res/layout/drawer_spacer.xml b/packages/SettingsLib/res/layout/drawer_spacer.xml deleted file mode 100644 index 98120cf904d0..000000000000 --- a/packages/SettingsLib/res/layout/drawer_spacer.xml +++ /dev/null @@ -1,20 +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. ---> -<Space - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/spacer" - android:layout_width="match_parent" - android:layout_height="@dimen/drawer_spacer_height" /> diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml index b659cee03780..e9c175f6fed0 100644 --- a/packages/SettingsLib/res/layout/settings_with_drawer.xml +++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml @@ -47,13 +47,4 @@ android:layout_height="fill_parent" android:background="?android:attr/windowBackground" /> </LinearLayout> - <!-- The navigation drawer --> - <ListView android:id="@+id/left_drawer" - android:layout_width="@dimen/drawer_width" - android:layout_height="match_parent" - android:layout_gravity="start" - android:choiceMode="singleChoice" - android:divider="@android:color/transparent" - android:dividerHeight="0dp" - android:background="?android:attr/colorBackground" /> </android.support.v4.widget.DrawerLayout> diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml index ee69b56ef472..64f21b50c0bc 100755 --- a/packages/SettingsLib/res/values/config.xml +++ b/packages/SettingsLib/res/values/config.xml @@ -26,9 +26,6 @@ <!-- Whether to send a custom package name with the PSD.--> <bool name="config_sendPackageName">false</bool> - <!-- Whether to enable the left nav drawer in all Settings UI.--> - <bool name="config_enable_nav_drawer">false</bool> - <!-- Name for the set of keys associating package names --> <string name="config_helpPackageNameKey" translatable="false"></string> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index aa3661707130..3322839ee213 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -51,12 +51,6 @@ <dimen name="usage_graph_dot_size">.75dp</dimen> <dimen name="usage_graph_dot_interval">7dp</dimen> - <dimen name="drawer_icon_size">24dp</dimen> - <dimen name="normal_icon_size">24dp</dimen> - <dimen name="drawer_icon_margin">24dp</dimen> - <dimen name="drawer_width">300dp</dimen> - <dimen name="drawer_item_top_bottom_margin">4dp</dimen> - <dimen name="drawer_spacer_height">32dp</dimen> <dimen name="battery_height">14.5dp</dimen> <dimen name="battery_width">9.5dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index a8cab17b0fac..457ce76d79ac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -5,7 +5,7 @@ * 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, @@ -18,7 +18,6 @@ package com.android.settingslib.drawer; import android.annotation.LayoutRes; import android.annotation.Nullable; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -29,20 +28,15 @@ import android.content.res.TypedArray; import android.os.AsyncTask; import android.os.Bundle; import android.provider.Settings; -import android.support.v4.widget.DrawerLayout; import android.util.ArraySet; import android.util.Log; import android.util.Pair; -import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager.LayoutParams; -import android.widget.AdapterView; import android.widget.FrameLayout; -import android.widget.ListView; import android.widget.Toolbar; import com.android.settingslib.R; @@ -67,10 +61,7 @@ public class SettingsDrawerActivity extends Activity { private final PackageReceiver mPackageReceiver = new PackageReceiver(); private final List<CategoryListener> mCategoryListeners = new ArrayList<>(); - private SettingsDrawerAdapter mDrawerAdapter; private FrameLayout mContentHeaderContainer; - private DrawerLayout mDrawerLayout; - private boolean mShowingMenu; // Remove below after new IA @Deprecated @@ -94,122 +85,50 @@ public class SettingsDrawerActivity extends Activity { } super.setContentView(R.layout.settings_with_drawer); mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container); - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - if (mDrawerLayout == null) { - return; - } + Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar); if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) { toolbar.setVisibility(View.GONE); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - mDrawerLayout = null; return; } - if (!isNavDrawerEnabled()) { - setIsDrawerPresent(false); - } - if (!isDashboardFeatureEnabled()) { - getDashboardCategories(); - } setActionBar(toolbar); - mDrawerAdapter = new SettingsDrawerAdapter(this); - ListView listView = (ListView) findViewById(R.id.left_drawer); - listView.setAdapter(mDrawerAdapter); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - public void onItemClick(android.widget.AdapterView<?> parent, View view, int position, - long id) { - onTileClicked(mDrawerAdapter.getTile(position)); - } - }); - if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) - + " ms"); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (mShowingMenu && mDrawerLayout != null && item.getItemId() == android.R.id.home - && mDrawerAdapter.getCount() != 0) { - openDrawer(); - return true; + if (DEBUG_TIMING) { + Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + + " ms"); } - return super.onOptionsItemSelected(item); } @Override public boolean onNavigateUp() { - if (!isNavDrawerEnabled()) { - finish(); - return true; - } - return super.onNavigateUp(); + finish(); + return true; } @Override protected void onResume() { super.onResume(); - - if (mDrawerLayout != null) { - final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - filter.addAction(Intent.ACTION_PACKAGE_REPLACED); - filter.addDataScheme("package"); - registerReceiver(mPackageReceiver, filter); - - if (isDashboardFeatureEnabled()) { - new CategoriesUpdateTask().execute(); - } else { - new CategoriesUpdater().execute(); - } - } + final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); + filter.addDataScheme("package"); + registerReceiver(mPackageReceiver, filter); + + new CategoriesUpdateTask().execute(); final Intent intent = getIntent(); - if (intent != null) { - if (intent.hasExtra(EXTRA_SHOW_MENU)) { - if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) { - // Intent explicitly set to show menu. - showMenuIcon(); - } - } else if (isNavDrawerEnabled() && isTopLevelTile(intent)) { - showMenuIcon(); - } + if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) { + // Intent explicitly set to show menu. + showMenuIcon(); } } @Override protected void onPause() { - if (mDrawerLayout != null) { - unregisterReceiver(mPackageReceiver); - } - + unregisterReceiver(mPackageReceiver); super.onPause(); } - private boolean isTopLevelTile(Intent intent) { - final ComponentName componentName = intent.getComponent(); - if (componentName == null) { - return false; - } - if (isDashboardFeatureEnabled()) { - final DashboardCategory homepageCategories = CategoryManager.get(this) - .getTilesByCategory(this, CategoryKey.CATEGORY_HOMEPAGE, getSettingPkg()); - return homepageCategories == - null ? false : homepageCategories.containsComponent(componentName); - } else { - // Look for a tile that has the same component as incoming intent - final List<DashboardCategory> categories = getDashboardCategories(); - for (DashboardCategory category : categories) { - if (category.containsComponent(componentName)) { - return true; - } - } - if (DEBUG) { - Log.d(TAG, "Intent is not for top level settings " + intent); - } - return false; - } - } - /** * Gets the name of the intent action of the default setting app. Used to launch setting app * when Settings Home is clicked. @@ -226,30 +145,6 @@ public class SettingsDrawerActivity extends Activity { mCategoryListeners.remove(listener); } - public void setIsDrawerPresent(boolean isPresent) { - if (isPresent) { - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - updateDrawer(); - } else { - if (mDrawerLayout != null) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - mDrawerLayout = null; - } - } - } - - public void openDrawer() { - if (mDrawerLayout != null) { - mDrawerLayout.openDrawer(Gravity.START); - } - } - - public void closeDrawer() { - if (mDrawerLayout != null) { - mDrawerLayout.closeDrawers(); - } - } - public void setContentHeaderView(View headerView) { mContentHeaderContainer.removeAllViews(); if (headerView != null) { @@ -276,31 +171,8 @@ public class SettingsDrawerActivity extends Activity { ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params); } - public void updateDrawer() { - if (mDrawerLayout == null) { - return; - } - // TODO: Do this in the background with some loading. - if (isDashboardFeatureEnabled()) { - mDrawerAdapter.updateHomepageCategories(getSettingPkg()); - } else { - mDrawerAdapter.updateCategories(); - } - if (mDrawerAdapter.getCount() != 0) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - } else { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - } - } - public void showMenuIcon() { getActionBar().setDisplayHomeAsUpEnabled(true); - if (isNavDrawerEnabled()) { - mShowingMenu = true; - getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); - getActionBar().setHomeActionContentDescription( - R.string.content_description_menu_button); - } } public List<DashboardCategory> getDashboardCategories() { @@ -315,51 +187,12 @@ public class SettingsDrawerActivity extends Activity { } protected void onCategoriesChanged() { - updateDrawer(); final int N = mCategoryListeners.size(); for (int i = 0; i < N; i++) { mCategoryListeners.get(i).onCategoriesChanged(); } } - @Deprecated - public boolean openTile(Tile tile) { - closeDrawer(); - if (tile == null) { - Intent intent = new Intent(getSettingAction()).addFlags( - Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); - return true; - } - try { - ProfileSelectDialog.updateUserHandlesIfNeeded(this /* context */, tile); - int numUserHandles = tile.userHandle.size(); - if (numUserHandles > 1) { - ProfileSelectDialog.show(getFragmentManager(), tile); - return false; - } else if (numUserHandles == 1) { - // Show menu on top level items. - tile.intent.putExtra(EXTRA_SHOW_MENU, true); - tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivityAsUser(tile.intent, tile.userHandle.get(0)); - } else { - // Show menu on top level items. - tile.intent.putExtra(EXTRA_SHOW_MENU, true); - tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(tile.intent); - } - } catch (ActivityNotFoundException e) { - Log.w(TAG, "Couldn't find tile " + tile.intent, e); - } - return true; - } - - protected void onTileClicked(Tile tile) { - if (openTile(tile)) { - finish(); - } - } - public void onProfileTileOpen() { finish(); } @@ -375,8 +208,8 @@ public class SettingsDrawerActivity extends Activity { sTileBlacklist.add(component); } pm.setComponentEnabledSetting(component, enabled - ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED - : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } @@ -385,11 +218,7 @@ public class SettingsDrawerActivity extends Activity { * Updates dashboard categories. Only necessary to call this after setTileEnabled */ public void updateCategories() { - if (isDashboardFeatureEnabled()) { - new CategoriesUpdateTask().execute(); - } else { - new CategoriesUpdater().execute(); - } + new CategoriesUpdateTask().execute(); } public String getSettingPkg() { @@ -400,42 +229,6 @@ public class SettingsDrawerActivity extends Activity { void onCategoriesChanged(); } - /** - * @deprecated remove after new IA - */ - @Deprecated - private class CategoriesUpdater extends AsyncTask<Void, Void, List<DashboardCategory>> { - @Override - protected List<DashboardCategory> doInBackground(Void... params) { - if (sConfigTracker.applyNewConfig(getResources())) { - sTileCache.clear(); - } - return TileUtils.getCategories(SettingsDrawerActivity.this, sTileCache); - } - - @Override - protected void onPreExecute() { - if (sConfigTracker == null || sTileCache == null) { - getDashboardCategories(); - } - } - - @Override - protected void onPostExecute(List<DashboardCategory> dashboardCategories) { - for (int i = 0; i < dashboardCategories.size(); i++) { - DashboardCategory category = dashboardCategories.get(i); - for (int j = 0; j < category.tiles.size(); j++) { - Tile tile = category.tiles.get(j); - if (sTileBlacklist.contains(tile.intent.getComponent())) { - category.tiles.remove(j--); - } - } - } - sDashboardCategories = dashboardCategories; - onCategoriesChanged(); - } - } - private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> { private final CategoryManager mCategoryManager; @@ -457,25 +250,10 @@ public class SettingsDrawerActivity extends Activity { } } - /** - * @return {@code true} if IA (Information Architecture) is enabled. - */ - protected boolean isDashboardFeatureEnabled() { - return false; - } - - boolean isNavDrawerEnabled() { - return getResources().getBoolean(R.bool.config_enable_nav_drawer); - } - private class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (isDashboardFeatureEnabled()) { - new CategoriesUpdateTask().execute(); - } else { - new CategoriesUpdater().execute(); - } + new CategoriesUpdateTask().execute(); } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java deleted file mode 100644 index 75942f93b67b..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 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.drawer; - -import android.graphics.drawable.Icon; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.settingslib.R; - -import java.util.ArrayList; -import java.util.List; - -public class SettingsDrawerAdapter extends BaseAdapter { - - private final ArrayList<Item> mItems = new ArrayList<>(); - private final SettingsDrawerActivity mActivity; - - public SettingsDrawerAdapter(SettingsDrawerActivity activity) { - mActivity = activity; - } - - /** - * @deprecated Remove after new IA - */ - @Deprecated - void updateCategories() { - List<DashboardCategory> categories = mActivity.getDashboardCategories(); - mItems.clear(); - // Spacer. - mItems.add(null); - Item tile = new Item(); - tile.label = mActivity.getString(R.string.home); - tile.icon = Icon.createWithResource(mActivity, R.drawable.home); - mItems.add(tile); - for (int i = 0; i < categories.size(); i++) { - Item category = new Item(); - category.icon = null; - DashboardCategory dashboardCategory = categories.get(i); - category.label = dashboardCategory.title; - mItems.add(category); - for (int j = 0; j < dashboardCategory.tiles.size(); j++) { - tile = new Item(); - Tile dashboardTile = dashboardCategory.tiles.get(j); - tile.label = dashboardTile.title; - tile.icon = dashboardTile.icon; - tile.tile = dashboardTile; - mItems.add(tile); - } - } - notifyDataSetChanged(); - } - - public void updateHomepageCategories(String settingPkg) { - final DashboardCategory category = CategoryManager.get(mActivity) - .getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE, settingPkg); - mItems.clear(); - // Spacer. - mItems.add(null); - Item tile = new Item(); - tile.label = mActivity.getString(R.string.home); - tile.icon = Icon.createWithResource(mActivity, R.drawable.home); - mItems.add(tile); - for (int j = 0; j < category.tiles.size(); j++) { - tile = new Item(); - Tile dashboardTile = category.tiles.get(j); - tile.label = dashboardTile.title; - tile.icon = dashboardTile.icon; - tile.tile = dashboardTile; - mItems.add(tile); - } - notifyDataSetChanged(); - } - - public Tile getTile(int position) { - return mItems.get(position) != null ? mItems.get(position).tile : null; - } - - @Override - public int getCount() { - return mItems.size(); - } - - @Override - public Object getItem(int position) { - return mItems.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public boolean isEnabled(int position) { - return mItems.get(position) != null && mItems.get(position).icon != null; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Item item = mItems.get(position); - if (item == null) { - if (convertView == null || convertView.getId() != R.id.spacer) { - convertView = LayoutInflater.from(mActivity).inflate(R.layout.drawer_spacer, - parent, false); - } - return convertView; - } - if (convertView != null && convertView.getId() == R.id.spacer) { - convertView = null; - } - boolean isTile = item.icon != null; - if (convertView == null || (isTile != (convertView.getId() == R.id.tile_item))) { - convertView = LayoutInflater.from(mActivity).inflate(isTile ? R.layout.drawer_item - : R.layout.drawer_category, - parent, false); - } - if (isTile) { - ((ImageView) convertView.findViewById(android.R.id.icon)).setImageIcon(item.icon); - } - ((TextView) convertView.findViewById(android.R.id.title)).setText(item.label); - return convertView; - } - - private static class Item { - public Icon icon; - public CharSequence label; - public Tile tile; - } -} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java index c3f2f736b441..752b5b0e152c 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java @@ -86,16 +86,6 @@ public class SettingsDrawerActivityTest { .check(matches(isDisplayed())); } - @Test - public void startActivity_shouldNotHaveNavDrawer() { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - Activity activity = instrumentation.startActivitySync( - new Intent(instrumentation.getTargetContext(), TestActivity.class)); - - assertThat(((SettingsDrawerActivity) activity).isNavDrawerEnabled()) - .isFalse(); - } - /** * Test Activity in this test. * diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index f31555327b01..e8df38bb964b 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -167,7 +167,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String toString() { - return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index b95ed08eeb27..69e481fc9e1c 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -28,6 +28,7 @@ import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import com.android.internal.os.TransferPipe; @@ -1262,13 +1263,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Bundle extras = new Bundle(); extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true); - mImeSwitcherNotification = new Notification.Builder(mContext) - .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default) - .setWhen(0) - .setOngoing(true) - .addExtras(extras) - .setCategory(Notification.CATEGORY_SYSTEM) - .setColor(com.android.internal.R.color.system_notification_accent_color); + mImeSwitcherNotification = + new Notification.Builder(mContext, SystemNotificationChannels.VIRTUAL_KEYBOARD) + .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default) + .setWhen(0) + .setOngoing(true) + .addExtras(extras) + .setCategory(Notification.CATEGORY_SYSTEM) + .setColor(com.android.internal.R.color.system_notification_accent_color); Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index f76ddc71bc01..0a9610f36abb 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -76,6 +76,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.ILockSettings; @@ -442,21 +443,20 @@ public class LockSettingsService extends ILockSettings.Stub { // Suppress all notifications on non-FBE devices for now if (!StorageManager.isFileEncryptedNativeOrEmulated()) return; - Notification notification = new Notification.Builder(mContext) - .setSmallIcon(com.android.internal.R.drawable.ic_user_secure) - .setWhen(0) - .setOngoing(true) - .setTicker(title) - .setDefaults(0) // please be quiet - .setPriority(Notification.PRIORITY_MAX) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setContentText(message) - .setSubText(detail) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setContentIntent(intent) - .build(); + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.SECURITY) + .setSmallIcon(com.android.internal.R.drawable.ic_user_secure) + .setWhen(0) + .setOngoing(true) + .setTicker(title) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(message) + .setSubText(detail) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setContentIntent(intent) + .build(); mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user); } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index d54ebaafb5bb..b83dbd686ae5 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -663,12 +663,12 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public boolean setActiveScorer(String packageName) { // Only the system can set the active scorer - if (isCallerSystemProcess(getCallingUid()) || callerCanRequestScores()) { - return mNetworkScorerAppManager.setActiveScorer(packageName); - } else { + if (!isCallerSystemProcess(getCallingUid()) || !callerCanRequestScores()) { throw new SecurityException( "Caller is neither the system process nor a score requester."); } + + return mNetworkScorerAppManager.setActiveScorer(packageName); } /** @@ -732,23 +732,23 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public List<NetworkScorerAppData> getAllValidScorers() { // Only the system can access this data. - if (isCallerSystemProcess(getCallingUid()) || callerCanRequestScores()) { - return mNetworkScorerAppManager.getAllValidScorers(); - } else { + if (!isCallerSystemProcess(getCallingUid()) || !callerCanRequestScores()) { throw new SecurityException( "Caller is neither the system process nor a score requester."); } + + return mNetworkScorerAppManager.getAllValidScorers(); } @Override public void disableScoring() { // Only the active scorer or the system should be allowed to disable scoring. - if (isCallerActiveScorer(getCallingUid()) || callerCanRequestScores()) { - // no-op for now but we could write to the setting if needed. - } else { + if (!isCallerActiveScorer(getCallingUid()) || !callerCanRequestScores()) { throw new SecurityException( "Caller is neither the active scorer nor the scorer manager."); } + + // no-op for now but we could write to the setting if needed. } /** Clear scores. Callers are responsible for checking permissions as appropriate. */ diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index acacb9e0be70..5115fdecd1bf 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -58,6 +58,7 @@ import java.util.Collections; import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; +import com.android.internal.notification.SystemNotificationChannels; import com.android.server.power.ShutdownThread; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; @@ -739,7 +740,8 @@ final class UiModeManagerService extends SystemService { if (mCarModeEnabled) { Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); - Notification.Builder n = new Notification.Builder(context) + Notification.Builder n = + new Notification.Builder(context, SystemNotificationChannels.CAR_MODE) .setSmallIcon(R.drawable.stat_notify_car_mode) .setDefaults(Notification.DEFAULT_LIGHTS) .setOngoing(true) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 1b2c75d50014..dc73b6385897 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -92,6 +92,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -2780,16 +2781,17 @@ public class AccountManagerService } UserHandle user = UserHandle.of(userId); Context contextForUser = getContextForUser(user); - Notification n = new Notification.Builder(contextForUser) - .setSmallIcon(android.R.drawable.stat_sys_warning) - .setWhen(0) - .setColor(contextForUser.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setContentText(subtitle) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, user)) - .build(); + Notification n = + new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setWhen(0) + .setColor(contextForUser.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(subtitle) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, user)) + .build(); installNotification(getCredentialPermissionNotificationId( account, authTokenType, uid), n, packageName, user.getIdentifier()); } @@ -4844,7 +4846,8 @@ public class AccountManagerService final String notificationTitleFormat = contextForUser.getText(R.string.notification_title).toString(); - Notification n = new Notification.Builder(contextForUser) + Notification n = + new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) .setWhen(0) .setSmallIcon(android.R.drawable.stat_sys_warning) .setColor(contextForUser.getColor( @@ -4864,6 +4867,7 @@ public class AccountManagerService private void installNotification(int notificationId, final Notification notification, String packageName, int userId) { + SystemNotificationChannels.createAccountChannelForPackage(packageName, mContext); final long token = clearCallingIdentity(); try { INotificationManager notificationManager = mInjector.getNotificationManager(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2bc131fae905..b4f8f61971f8 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2810,14 +2810,13 @@ public final class ActiveServices { } List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, int flags, - int callingUid, boolean allowed) { + int callingUid, boolean allowed, boolean canInteractAcrossUsers) { ArrayList<ActivityManager.RunningServiceInfo> res = new ArrayList<ActivityManager.RunningServiceInfo>(); final long ident = Binder.clearCallingIdentity(); try { - if (ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid) - == PERMISSION_GRANTED) { + if (canInteractAcrossUsers) { int[] users = mAm.mUserController.getUsers(); for (int ui=0; ui<users.length && res.size() < maxNum; ui++) { ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(users[ui]); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 67cac981f599..2f2b533a531d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -314,6 +314,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import com.android.internal.notification.SystemNotificationChannels; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -1972,7 +1973,8 @@ public class ActivityManagerService extends IActivityManager.Stub Context context = mContext.createPackageContext(process.info.packageName, 0); String text = mContext.getString(R.string.heavy_weight_notification, context.getApplicationInfo().loadLabel(context.getPackageManager())); - Notification notification = new Notification.Builder(context) + Notification notification = + new Notification.Builder(context, SystemNotificationChannels.DEVELOPER) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) .setWhen(0) .setOngoing(true) @@ -2222,7 +2224,8 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage); } int userId = UserHandle.getUserId(uid); - Notification notification = new Notification.Builder(mContext) + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) .setWhen(0) .setOngoing(true) @@ -17506,12 +17509,15 @@ public class ActivityManagerService extends IActivityManager.Stub public List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags) { enforceNotIsolatedCaller("getServices"); - synchronized (this) { - final int callingUid = Binder.getCallingUid(); - final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(), - callingUid); - return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid, allowed); + final int callingUid = Binder.getCallingUid(); + final boolean canInteractAcrossUsers = (ActivityManager.checkUidPermission( + INTERACT_ACROSS_USERS_FULL, callingUid) == PERMISSION_GRANTED); + final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(), + callingUid); + synchronized (this) { + return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid, + allowed, canInteractAcrossUsers); } } diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index f4f6b661c0e1..e0d3abd03988 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -35,6 +35,7 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ProgressReporter; import com.android.server.UiThread; @@ -144,13 +145,13 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { contentIntent = null; } - final Notification notif = new Notification.Builder(mService.mContext) + final Notification notif = + new Notification.Builder(mService.mContext, + SystemNotificationChannels.UPDATES) .setSmallIcon(R.drawable.stat_sys_adb) .setWhen(0) .setOngoing(true) .setTicker(title) - .setDefaults(0) - .setPriority(Notification.PRIORITY_MAX) .setColor(context.getColor( com.android.internal.R.color.system_notification_accent_color)) .setContentTitle(title) diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 82b00da5b809..dfbe59f1c8f2 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -115,7 +115,7 @@ final class ServiceRecord extends Binder { long destroyTime; // time at which destory was initiated. String stringName; // caching of toString - + private int lastStartId; // identifier of most recent start request. static class StartItem { @@ -203,7 +203,7 @@ final class ServiceRecord extends Binder { } } } - + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("intent={"); pw.print(intent.getIntent().toShortString(false, true, false, true)); @@ -413,7 +413,7 @@ final class ServiceRecord extends Binder { restartDelay = 0; restartTime = 0; } - + public StartItem findDeliveredStart(int id, boolean remove) { final int N = deliveredStarts.size(); for (int i=0; i<N; i++) { @@ -423,10 +423,10 @@ final class ServiceRecord extends Binder { return si; } } - + return null; } - + public int getLastStartId() { return lastStartId; } @@ -478,7 +478,8 @@ final class ServiceRecord extends Binder { ctx = ams.mContext.createPackageContextAsUser( appInfo.packageName, 0, new UserHandle(userId)); - Notification.Builder notiBuilder = new Notification.Builder(ctx); + Notification.Builder notiBuilder = new Notification.Builder(ctx, + localForegroundNoti.getChannel()); // it's ugly, but it clearly identifies the app notiBuilder.setSmallIcon(appInfo.icon); @@ -486,9 +487,6 @@ final class ServiceRecord extends Binder { // mark as foreground notiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true); - // we are doing the app a kindness here - notiBuilder.setPriority(Notification.PRIORITY_MIN); - Intent runningIntent = new Intent( Settings.ACTION_APPLICATION_DETAILS_SETTINGS); runningIntent.setData(Uri.fromParts("package", @@ -541,7 +539,7 @@ final class ServiceRecord extends Binder { }); } } - + public void cancelNotification() { // Do asynchronous communication with notification manager to // avoid deadlocks. @@ -588,7 +586,7 @@ final class ServiceRecord extends Binder { } }); } - + public void clearDeliveredStartsLocked() { for (int i=deliveredStarts.size()-1; i>=0; i--) { deliveredStarts.get(i).removeUriPermissionsLocked(); diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index d648dd82dc9a..4404dcf5e01c 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -152,9 +152,11 @@ public final class PlaybackActivityMonitor synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (checkConfigurationCaller(piid, apc, binderUid)) { - apc.getPlayerProxy().applyVolumeShaper( - DUCK_ID, - TERMINATE); + try { + apc.getPlayerProxy().applyVolumeShaper( + DUCK_ID, + TERMINATE); + } catch (Exception e) { /* silent failure, happens happens with binder failure */ } mPlayers.remove(new Integer(piid)); } else { Log.e(TAG, "Error releasing player " + piid); diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 68fe5053b585..83751a9c15df 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -32,6 +32,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.notification.SystemNotificationChannels; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -187,7 +188,9 @@ public class NetworkNotificationManager { return; } - Notification.Builder builder = new Notification.Builder(mContext) + final String channelId = highPriority ? SystemNotificationChannels.NETWORK_ALERTS : + SystemNotificationChannels.NETWORK_STATUS; + Notification.Builder builder = new Notification.Builder(mContext, channelId) .setWhen(System.currentTimeMillis()) .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH) .setSmallIcon(icon) @@ -198,10 +201,6 @@ public class NetworkNotificationManager { .setContentTitle(title) .setContentIntent(intent) .setLocalOnly(true) - .setPriority(highPriority ? - Notification.PRIORITY_HIGH : - Notification.PRIORITY_DEFAULT) - .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0) .setOnlyAlertOnce(true); if (notifyType == NotificationType.NETWORK_SWITCH) { diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 6c608a28089d..39e3758393df 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -66,6 +66,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.IndentingPrintWriter; @@ -668,7 +669,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering tethered_notification_message); if (mTetheredNotificationBuilder == null) { - mTetheredNotificationBuilder = new Notification.Builder(mContext); + mTetheredNotificationBuilder = + new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS); mTetheredNotificationBuilder.setWhen(0) .setOngoing(true) .setColor(mContext.getColor( @@ -950,6 +952,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering // Events from NetworkCallbacks that we process on the master state // machine thread on behalf of the UpstreamNetworkMonitor. static final int EVENT_UPSTREAM_CALLBACK = BASE_MASTER + 5; + // we treated the error and want now to clear it + static final int CMD_CLEAR_ERROR = BASE_MASTER + 6; private State mInitialState; private State mTetherModeAliveState; @@ -1491,6 +1495,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; who.sendMessage(mErrorNotification); break; + case CMD_CLEAR_ERROR: + mErrorNotification = ConnectivityManager.TETHER_ERROR_NO_ERROR; + transitionTo(mInitialState); + break; default: retValue = false; } @@ -1635,6 +1643,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering // Not really very much we can do here. } + // If TetherMasterSM is in ErrorState, TetherMasterSM stays there. + // Thus we give a chance for TetherMasterSM to recover to InitialState + // by sending CMD_CLEAR_ERROR + if (error == ConnectivityManager.TETHER_ERROR_MASTER_ERROR) { + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who); + } switch (state) { case IControlsTethering.STATE_UNAVAILABLE: case IControlsTethering.STATE_AVAILABLE: diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index b963555124ab..9fc2fc7bb31e 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -85,6 +85,7 @@ import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; +import com.android.internal.notification.SystemNotificationChannels; import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -1293,17 +1294,16 @@ public class Vpn { mContext, /* request */ 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, null, user); - final Notification.Builder builder = new Notification.Builder(mContext) - .setDefaults(0) - .setSmallIcon(R.drawable.vpn_connected) - .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected)) - .setContentText(mContext.getString(R.string.vpn_lockdown_config)) - .setContentIntent(configIntent) - .setCategory(Notification.CATEGORY_SYSTEM) - .setPriority(Notification.PRIORITY_LOW) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setOngoing(true) - .setColor(mContext.getColor(R.color.system_notification_accent_color)); + final Notification.Builder builder = + new Notification.Builder(mContext, SystemNotificationChannels.VPN) + .setSmallIcon(R.drawable.vpn_connected) + .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected)) + .setContentText(mContext.getString(R.string.vpn_lockdown_config)) + .setContentIntent(configIntent) + .setCategory(Notification.CATEGORY_SYSTEM) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setOngoing(true) + .setColor(mContext.getColor(R.color.system_notification_accent_color)); notificationManager.notifyAsUser(TAG, 0, builder.build(), user); } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 5e5157913f20..710ab33874cd 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -167,7 +167,8 @@ public class TetherInterfaceStateMachine extends StateMachine { private void maybeLogMessage(State state, int what) { if (DBG) { Log.d(TAG, state.getName() + " got " + - sMagicDecoderRing.get(what, Integer.toString(what))); + sMagicDecoderRing.get(what, Integer.toString(what)) + ", Iface = " + + mIfaceName); } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 5b539ff1976d..bbad493a913f 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -81,6 +81,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerInternal; @@ -3250,7 +3251,8 @@ public class SyncManager { R.string.contentServiceTooManyDeletesNotificationDesc); Context contextForUser = getContextForUser(user); - Notification notification = new Notification.Builder(contextForUser) + Notification notification = + new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) .setSmallIcon(R.drawable.stat_notify_sync_error) .setTicker(mContext.getString(R.string.contentServiceSync)) .setWhen(System.currentTimeMillis()) @@ -3460,4 +3462,4 @@ public class SyncManager { return mContext; } } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java index 5a1e44574e13..43bb21d2b44a 100644 --- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java +++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java @@ -68,7 +68,9 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { mIsRestricted = restricted; mOwner = owner; try { - token.linkToDeath(this, 0); + if (token != null) { + token.linkToDeath(this, 0); + } } catch (RemoteException e) { Slog.w(TAG, "caught remote exception in linkToDeath: ", e); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 3793b911a038..297d5bd1afc9 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -23,6 +23,7 @@ import android.os.ShellCallback; import android.util.Log; import android.view.Display; import com.android.internal.inputmethod.InputMethodSubtypeHandle; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.R; import com.android.internal.util.Preconditions; @@ -973,17 +974,17 @@ public class InputManagerService extends IInputManager.Stub intent, 0, null, UserHandle.CURRENT); Resources r = mContext.getResources(); - Notification notification = new Notification.Builder(mContext) - .setContentTitle(r.getString( - R.string.select_keyboard_layout_notification_title)) - .setContentText(r.getString( - R.string.select_keyboard_layout_notification_message)) - .setContentIntent(keyboardLayoutIntent) - .setSmallIcon(R.drawable.ic_settings_language) - .setPriority(Notification.PRIORITY_LOW) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .build(); + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.PHYSICAL_KEYBOARD) + .setContentTitle(r.getString( + R.string.select_keyboard_layout_notification_title)) + .setContentText(r.getString( + R.string.select_keyboard_layout_notification_message)) + .setContentIntent(keyboardLayoutIntent) + .setSmallIcon(R.drawable.ic_settings_language) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .build(); mNotificationManager.notifyAsUser(null, R.string.select_keyboard_layout_notification_title, notification, UserHandle.ALL); diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 4a8539aa3282..a5e7d7c7b945 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -47,6 +47,7 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.Preconditions; import com.android.server.ConnectivityService; import com.android.server.EventLogTags; @@ -330,18 +331,18 @@ public class LockdownVpnTracker { } private void showNotification(int titleRes, int iconRes) { - final Notification.Builder builder = new Notification.Builder(mContext) - .setWhen(0) - .setSmallIcon(iconRes) - .setContentTitle(mContext.getString(titleRes)) - .setContentText(mContext.getString(R.string.vpn_lockdown_config)) - .setContentIntent(mConfigIntent) - .setPriority(Notification.PRIORITY_LOW) - .setOngoing(true) - .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), - mResetIntent) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); + final Notification.Builder builder = + new Notification.Builder(mContext, SystemNotificationChannels.VPN) + .setWhen(0) + .setSmallIcon(iconRes) + .setContentTitle(mContext.getString(titleRes)) + .setContentText(mContext.getString(R.string.vpn_lockdown_config)) + .setContentIntent(mConfigIntent) + .setOngoing(true) + .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), + mResetIntent) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); NotificationManager.from(mContext).notify(TAG, 0, builder.build()); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ac3a025a6fc0..507899836ee2 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -58,6 +58,8 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; +import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; +import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; @@ -169,6 +171,7 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; @@ -1067,7 +1070,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) { final String tag = buildNotificationTag(policy, type); - final Notification.Builder builder = new Notification.Builder(mContext); + final Notification.Builder builder = + new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS); builder.setOnlyAlertOnce(true); builder.setWhen(0L); builder.setColor(mContext.getColor( @@ -1085,7 +1089,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setContentTitle(title); builder.setContentText(body); builder.setDefaults(Notification.DEFAULT_ALL); - builder.setPriority(Notification.PRIORITY_HIGH); + builder.setChannel(SystemNotificationChannels.NETWORK_ALERTS); final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template); builder.setDeleteIntent(PendingIntent.getBroadcast( @@ -2527,14 +2531,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { - return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; - } - - static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) { - return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; - } - void updateRulesForPowerSaveUL() { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL"); try { @@ -2608,8 +2604,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method. private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) { if (enabled) { - if (isWhitelistedBatterySaverUL(uid) - || isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid))) { + if (isWhitelistedBatterySaverUL(uid) || isUidForegroundOnRestrictPowerUL(uid)) { setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW); } else { setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 45ff20bd3a6d..dae5da3c6ceb 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2736,9 +2736,10 @@ public class NotificationManagerService extends SystemService { Notification.EXTRA_BUILDER_APPLICATION_INFO); final Bundle extras = new Bundle(); extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo); + final String channelId = notificationRecord.getChannel().getId(); final Notification summaryNotification = - new Notification.Builder(getContext()).setSmallIcon( - adjustedSbn.getNotification().getSmallIcon()) + new Notification.Builder(getContext(), channelId) + .setSmallIcon(adjustedSbn.getNotification().getSmallIcon()) .setGroupSummary(true) .setGroup(GroupHelper.AUTOGROUP_KEY) .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true) diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 55a5f725f90b..0ae5f314d473 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -438,6 +438,7 @@ class InstantAppRegistry { bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); + icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); icon.draw(canvas); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a7a1683f8479..37f78b40c4ff 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -88,6 +88,7 @@ import libcore.io.IoUtils; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageHelper; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; @@ -1103,7 +1104,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { context.getResources().getDimensionPixelSize( android.R.dimen.notification_large_icon_height)); CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm); - return new Notification.Builder(context) + return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(R.drawable.ic_check_circle_24px) .setColor(context.getResources().getColor( R.color.system_notification_accent_color)) diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 1c5675ac010e..fd731c323a74 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -29,6 +29,7 @@ import static com.android.server.pm.PackageInstallerService.prepareExternalStage import static com.android.server.pm.PackageInstallerService.prepareStageDir; import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -45,6 +46,7 @@ import android.content.pm.PackageParser.ApkLite; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.os.Binder; import android.os.Bundle; import android.os.FileBridge; @@ -295,6 +297,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.active = mActiveCount.get() > 0; info.mode = params.mode; + info.installReason = params.installReason; info.sizeBytes = params.sizeBytes; info.appPackageName = params.appPackageName; info.appIcon = params.appIcon; @@ -1139,6 +1142,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); + + // Send broadcast to default launcher only if it's a new install + final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); + if (success && isNewInstall) { + UserManagerService ums = UserManagerService.getInstance(); + if (ums != null) { + final UserInfo parent = ums.getProfileParent(userId); + final int launcherUid = (parent != null) ? parent.id : userId; + final ComponentName launcherComponent = mPm.getDefaultHomeActivity(launcherUid); + if (launcherComponent != null) { + Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) + .putExtra(PackageInstaller.EXTRA_SESSION, generateInfo()) + .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) + .setPackage(launcherComponent.getPackageName()); + mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid)); + } + } + } + mCallback.onSessionFinished(this, success); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 116c0a353643..838098310caf 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -582,7 +582,8 @@ public class PackageManagerService extends IPackageManager.Stub { Manifest.permission.RECEIVE_MMS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_NUMBER); + Manifest.permission.READ_PHONE_NUMBER, + Manifest.permission.ANSWER_PHONE_CALLS); /** @@ -6317,6 +6318,7 @@ public class PackageManagerService extends IPackageManager.Stub { ephemeralInstaller.filter = new IntentFilter(intent.getAction()); ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); + ephemeralInstaller.instantAppAvailable = true; result.add(ephemeralInstaller); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -7310,12 +7312,17 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } - if (!isCallerSameApp(packageName)) { - return false; - } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { + final boolean returnAllowed = + ps != null + && (isCallerSameApp(packageName) + || mContext.checkCallingOrSelfPermission( + android.Manifest.permission.ACCESS_INSTANT_APPS) + == PERMISSION_GRANTED + || mInstantAppRegistry.isInstantAccessGranted( + userId, UserHandle.getAppId(Binder.getCallingUid()), ps.appId)); + if (returnAllowed) { return ps.getInstantApp(userId); } } @@ -12334,6 +12341,7 @@ public class PackageManagerService extends IPackageManager.Stub { } res.iconResourceId = info.icon; res.system = res.activityInfo.applicationInfo.isSystemApp(); + res.instantAppAvailable = userState.instantApp; return res; } @@ -16824,11 +16832,20 @@ public class PackageManagerService extends IPackageManager.Stub { res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } - // Check whether the newly-scanned package wants to define an already-defined perm int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); + + // Don't allow anyone but the platform to define ephemeral permissions. + if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0 + && !PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) { + Slog.w(TAG, "Package " + pkg.packageName + + " attempting to delcare ephemeral permission " + + perm.info.name + "; Removing ephemeral."); + perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_EPHEMERAL; + } + // Check whether the newly-scanned package wants to define an already-defined perm if (bp != null) { // If the defining package is signed with our cert, it's okay. This // also includes the "updating the same package" case, of course. @@ -19636,6 +19653,35 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId()); } + /** + * Report the 'Home' activity which is currently set as "always use this one". If non is set + * then reports the most likely home activity or null if there are more than one. + */ + public ComponentName getDefaultHomeActivity(int userId) { + List<ResolveInfo> allHomeCandidates = new ArrayList<>(); + ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId); + if (cn != null) { + return cn; + } + + // Find the launcher with the highest priority and return that component if there are no + // other home activity with the same priority. + int lastPriority = Integer.MIN_VALUE; + ComponentName lastComponent = null; + final int size = allHomeCandidates.size(); + for (int i = 0; i < size; i++) { + final ResolveInfo ri = allHomeCandidates.get(i); + if (ri.priority > lastPriority) { + lastComponent = ri.activityInfo.getComponentName(); + lastPriority = ri.priority; + } else if (ri.priority == lastPriority) { + // Two components found with same priority. + lastComponent = null; + } + } + return lastComponent; + } + private Intent getHomeIntent() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java index afdec9ffddc3..12836dbb4820 100644 --- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -17,6 +17,7 @@ package com.android.server.storage; import android.app.NotificationChannel; +import com.android.internal.notification.SystemNotificationChannels; import com.android.server.EventLogTags; import com.android.server.SystemService; import com.android.server.pm.InstructionSets; @@ -141,7 +142,7 @@ public class DeviceStorageMonitorService extends SystemService { */ static final String SERVICE = "devicestoragemonitor"; - private static final String NOTIFICATION_CHANNEL_ID = SERVICE; + private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv"; /** * Handler that checks the amount of disk space on the device and sends a @@ -388,14 +389,13 @@ public class DeviceStorageMonitorService extends SystemService { PackageManager packageManager = context.getPackageManager(); boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK); - int importance = isTv - ? NotificationManager.IMPORTANCE_HIGH // Do not change: this is TV-specific - : NotificationManager.IMPORTANCE_LOW; - notificationMgr.createNotificationChannel( - new NotificationChannel(NOTIFICATION_CHANNEL_ID, - context.getString( - com.android.internal.R.string.device_storage_monitor_notification_channel), - importance)); + if (isTv) { + notificationMgr.createNotificationChannel(new NotificationChannel( + TV_NOTIFICATION_CHANNEL_ID, + context.getString( + com.android.internal.R.string.device_storage_monitor_notification_channel), + NotificationManager.IMPORTANCE_HIGH)); + } publishBinderService(SERVICE, mRemoteService); publishLocalService(DeviceStorageMonitorInternal.class, mLocalService); @@ -495,21 +495,22 @@ public class DeviceStorageMonitorService extends SystemService { : com.android.internal.R.string.low_internal_storage_view_text_no_boot); PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0, null, UserHandle.CURRENT); - Notification notification = new Notification.Builder(context) - .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full) - .setTicker(title) - .setColor(context.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setContentText(details) - .setContentIntent(intent) - .setStyle(new Notification.BigTextStyle() - .bigText(details)) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setCategory(Notification.CATEGORY_SYSTEM) - .setChannel(NOTIFICATION_CHANNEL_ID) - .extend(new Notification.TvExtender()) - .build(); + Notification notification = + new Notification.Builder(context, SystemNotificationChannels.ALERTS) + .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full) + .setTicker(title) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(details) + .setContentIntent(intent) + .setStyle(new Notification.BigTextStyle() + .bigText(details)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setCategory(Notification.CATEGORY_SYSTEM) + .extend(new Notification.TvExtender() + .setChannel(TV_NOTIFICATION_CHANNEL_ID)) + .build(); notification.flags |= Notification.FLAG_NO_CLEAR; notificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification, UserHandle.ALL); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 22630428ad01..379e3cea405e 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -762,6 +762,16 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (canFreezeBounds()) { freezeBounds(); } + + // In the process of tearing down before relaunching, the app will + // try and clean up it's child surfaces. We need to prevent this from + // happening, so we sever the children, transfering their ownership + // from the client it-self to the parent surface (owned by us). + for (int i = mChildren.size() - 1; i >= 0; i--) { + final WindowState w = mChildren.get(i); + w.mWinAnimator.detachChildren(); + } + mPendingRelaunchCount++; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2a4dfc444fe2..eb10f0c2978c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2199,6 +2199,15 @@ public class WindowManagerService extends IWindowManager.Stub if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, transit); } + + // When we start the exit animation we take the Surface from the client + // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger + // side child surfaces, so they will remain preserved in their current state + // (rather than be cleaned up immediately by the app code). + SurfaceControl.openTransaction(); + winAnimator.detachChildren(); + SurfaceControl.closeTransaction(); + return focusMayChange; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 98598e1654dc..4b7133836db8 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -566,6 +566,20 @@ class WindowStateAnimator { if (!mDestroyPreservedSurfaceUponRedraw) { return; } + if (mSurfaceController != null) { + if (mPendingDestroySurface != null) { + // If we are preserving a surface but we aren't relaunching that means + // we are just doing an in-place switch. In that case any SurfaceFlinger side + // child layers need to be reparented to the new surface to make this + // transparent to the app. + if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) { + SurfaceControl.openTransaction(); + mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController); + SurfaceControl.closeTransaction(); + } + } + } + destroyDeferredSurfaceLocked(); mDestroyPreservedSurfaceUponRedraw = false; } @@ -1965,4 +1979,10 @@ class WindowStateAnimator { } return mForceScaleUntilResize; } + + void detachChildren() { + if (mSurfaceController != null) { + mSurfaceController.detachChildren(); + } + } } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index f8e74284fafd..f7d3343831bf 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -135,6 +135,20 @@ class WindowSurfaceController { } } + void reparentChildrenInTransaction(WindowSurfaceController other) { + if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other); + if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) { + mSurfaceControl.reparentChildren(other.getHandle()); + } + } + + void detachChildren() { + if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN"); + if (mSurfaceControl != null) { + mSurfaceControl.detachChildren(); + } + } + void hideInTransaction(String reason) { if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null); mHiddenForOtherReasons = true; diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 20b70a6344b4..36ae94b88b23 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -1144,7 +1144,7 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject } auto result = gnssHal->setCallback(gnssCbIface); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("SetCallback for Gnss Interface fails\n"); return JNI_FALSE; } @@ -1154,7 +1154,7 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject ALOGE("Unable to initialize GNSS Xtra interface\n"); } else { result = gnssXtraIface->setCallback(gnssXtraCbIface); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { gnssXtraIface = nullptr; ALOGE("SetCallback for Gnss Xtra Interface fails\n"); } @@ -1344,7 +1344,7 @@ static void android_location_GnssLocationProvider_inject_time(JNIEnv* /* env */, jlong time, jlong timeReference, jint uncertainty) { if (gnssHal != nullptr) { auto result = gnssHal->injectTime(time, timeReference, uncertainty); - if (!result || !result.isOk()) { + if (!result.isOk() || !result) { ALOGE("%s: Gnss injectTime() failed", __func__); } } @@ -1354,7 +1354,7 @@ static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env jobject /* obj */, jdouble latitude, jdouble longitude, jfloat accuracy) { if (gnssHal != nullptr) { auto result = gnssHal->injectLocation(latitude, longitude, accuracy); - if (!result || !result.isOk()) { + if (!result.isOk() || !result) { ALOGE("%s: Gnss injectLocation() failed", __func__); } } @@ -1391,7 +1391,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_open( const char *apnStr = env->GetStringUTFChars(apn, NULL); auto result = agnssIface->dataConnOpen(apnStr, static_cast<IAGnss::ApnIpType>(apnIpType)); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result){ ALOGE("%s: Failed to set APN and its IP type", __func__); } env->ReleaseStringUTFChars(apn, apnStr); @@ -1405,7 +1405,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_closed(JNIEnv* } auto result = agnssIface->dataConnClosed(); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("%s: Failed to close AGnss data connection", __func__); } } @@ -1418,7 +1418,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_failed(JNIEnv* } auto result = agnssIface->dataConnFailed(); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("%s: Failed to notify unavailability of AGnss data connection", __func__); } } @@ -1434,7 +1434,7 @@ static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, j auto result = agnssIface->setServer(static_cast<IAGnssCallback::AGnssType>(type), c_hostname, port); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("%s: Failed to set AGnss host name and port", __func__); } @@ -1512,13 +1512,13 @@ static void android_location_GnssLocationProvider_update_network_state(JNIEnv* e auto result = agnssRilIface->updateNetworkState(connected, static_cast<IAGnssRil::NetworkType>(type), roaming); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("updateNetworkState failed"); } const char *c_apn = env->GetStringUTFChars(apn, NULL); result = agnssRilIface->updateNetworkAvailability(available, c_apn); - if ((!result) || (!result.isOk())) { + if (!result.isOk() || !result) { ALOGE("updateNetworkAvailability failed"); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 17e6bba5f1c7..8b94ca067621 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -165,10 +165,10 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; -import com.android.internal.util.ParcelableString; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; @@ -4863,6 +4863,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** + * Determine whether DPMS should check if a delegate package is already installed before + * granting it new delegations via {@link #setDelegatedScopes}. + */ + private static boolean shouldCheckIfDelegatePackageIsInstalled(String delegatePackage, + int targetSdk, List<String> scopes) { + // 1) Never skip is installed check from N. + if (targetSdk >= Build.VERSION_CODES.N) { + return true; + } + // 2) Skip if DELEGATION_CERT_INSTALL is the only scope being given. + if (scopes.size() == 1 && scopes.get(0).equals(DELEGATION_CERT_INSTALL)) { + return false; + } + // 3) Skip if all previously granted scopes are being cleared. + if (scopes.isEmpty()) { + return false; + } + // Otherwise it should check that delegatePackage is installed. + return true; + } + + /** * Set the scopes of a device owner or profile owner delegate. * * @param who the device owner or profile owner. @@ -4888,8 +4910,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Ensure calling process is device/profile owner. getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N). - if (scopes.size() == 1 && scopes.get(0).equals(DELEGATION_CERT_INSTALL) || - getTargetSdk(who.getPackageName(), userId) >= Build.VERSION_CODES.N) { + if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage, + getTargetSdk(who.getPackageName(), userId), scopes)) { // Throw when the delegate package is not installed. if (!isPackageInstalledForUser(delegatePackage, userId)) { throw new IllegalArgumentException("Package " + delegatePackage @@ -5107,8 +5129,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final String currentPackage = policy.mDelegationMap.keyAt(i); final List<String> currentScopes = policy.mDelegationMap.valueAt(i); - if (!currentPackage.equals(delegatePackage) && currentScopes.remove(scope)) { - setDelegatedScopes(who, currentPackage, currentScopes); + if (!currentPackage.equals(delegatePackage) && currentScopes.contains(scope)) { + final List<String> newScopes = new ArrayList(currentScopes); + newScopes.remove(scope); + setDelegatedScopes(who, currentPackage, newScopes); } } } @@ -5269,13 +5293,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void sendWipeProfileNotification() { String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe); - Notification notification = new Notification.Builder(mContext) - .setSmallIcon(android.R.drawable.stat_sys_warning) - .setContentTitle(mContext.getString(R.string.work_profile_deleted)) - .setContentText(contentText) - .setColor(mContext.getColor(R.color.system_notification_accent_color)) - .setStyle(new Notification.BigTextStyle().bigText(contentText)) - .build(); + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setContentTitle(mContext.getString(R.string.work_profile_deleted)) + .setContentText(contentText) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setStyle(new Notification.BigTextStyle().bigText(contentText)) + .build(); mInjector.getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification); } @@ -10695,7 +10720,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { intent.setPackage("com.android.systemui"); final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, UserHandle.CURRENT); - Notification notification = new Notification.Builder(mContext) + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(R.drawable.ic_qs_network_logging) .setContentTitle(mContext.getString(R.string.network_logging_notification_title)) .setContentText(mContext.getString(R.string.network_logging_notification_text)) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java index 03c137a95b85..1933fe750e75 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java @@ -34,8 +34,8 @@ import android.provider.Settings; import android.security.KeyChain.KeyChainConnection; import android.util.Log; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.R; -import com.android.internal.util.ParcelableString; import java.util.ArrayList; import java.util.List; @@ -132,16 +132,16 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.of(parentUserId)); - final Notification noti = new Notification.Builder(userContext) - .setSmallIcon(smallIconId) - .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning, - pendingCertificateCount)) - .setContentText(contentText) - .setContentIntent(notifyIntent) - .setPriority(Notification.PRIORITY_HIGH) - .setShowWhen(false) - .setColor(R.color.system_notification_accent_color) - .build(); + final Notification noti = + new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) + .setSmallIcon(smallIconId) + .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning, + pendingCertificateCount)) + .setContentText(contentText) + .setContentIntent(notifyIntent) + .setShowWhen(false) + .setColor(R.color.system_notification_accent_color) + .build(); mInjector.getNotificationManager().notifyAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); @@ -150,12 +150,7 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi private List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { - List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList(); - List<String> result = new ArrayList<>(aliases.size()); - for (int i = 0; i < aliases.size(); i++) { - result.add(aliases.get(i).string); - } - return result; + return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java index 6d42dc9e4e5a..969c89eb4029 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java @@ -28,6 +28,7 @@ import android.provider.Settings; import android.text.format.DateUtils; import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -62,14 +63,14 @@ class RemoteBugreportUtils { PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type, dialogIntent, 0, null, UserHandle.CURRENT); - Notification.Builder builder = new Notification.Builder(context) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setOngoing(true) - .setLocalOnly(true) - .setPriority(Notification.PRIORITY_HIGH) - .setContentIntent(pendingDialogIntent) - .setColor(context.getColor( - com.android.internal.R.color.system_notification_accent_color)); + Notification.Builder builder = + new Notification.Builder(context, SystemNotificationChannels.DEVELOPER) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setOngoing(true) + .setLocalOnly(true) + .setContentIntent(pendingDialogIntent) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)); if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) { builder.setContentTitle(context.getString( diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e586482af83e..1a0aff79be6e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -53,6 +53,7 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.app.NightDisplayController; import com.android.internal.logging.MetricsLogger; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.policy.EmergencyAffordanceManager; @@ -1118,6 +1119,7 @@ public final class SystemServer { traceBeginAndSlog("StartNotificationManager"); mSystemServiceManager.startService(NotificationManagerService.class); + SystemNotificationChannels.createAll(context); notification = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); networkPolicy.bindNotificationManager(notification); diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java index f943ee2c09bf..472f98455d6a 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java +++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java @@ -61,6 +61,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.KeyValueListParser; import android.util.Slog; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -818,7 +819,7 @@ public class RetailDemoModeService extends SystemService { } Notification createResetNotification() { - return new Notification.Builder(getContext()) + return new Notification.Builder(getContext(), SystemNotificationChannels.RETAIL_MODE) .setContentTitle(getContext().getString(R.string.reset_retail_demo_mode_title)) .setContentText(getContext().getString(R.string.reset_retail_demo_mode_text)) .setOngoing(true) diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/notification/AndroidManifest.xml index 92f155f53420..cf050a89b0e6 100644 --- a/services/tests/notification/AndroidManifest.xml +++ b/services/tests/notification/AndroidManifest.xml @@ -23,6 +23,7 @@ <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.READ_CONTACTS" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java index 936531b07059..05c33a4e1cea 100644 --- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java @@ -64,7 +64,7 @@ public class GroupHelperTest { private StatusBarNotification getSbn(String pkg, int id, String tag, UserHandle user, String groupKey) { - Notification.Builder nb = new Notification.Builder(getContext()) + Notification.Builder nb = new Notification.Builder(getContext(), "test_channel_id") .setContentTitle("A") .setWhen(1205); if (groupKey != null) { diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java index 064ab0a3adc4..176342be99fd 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java @@ -63,6 +63,7 @@ public class NotificationComparatorTest { private final int smsUid = 11; private final String pkg2 = "pkg2"; private final int uid2 = 1111111; + private static final String TEST_CHANNEL_ID = "test_channel_id"; private NotificationRecord mRecordMinCall; private NotificationRecord mRecordHighCall; @@ -100,7 +101,7 @@ public class NotificationComparatorTest { smsPkg = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION); - Notification n1 = new Notification.Builder(mContext) + Notification n1 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); @@ -109,7 +110,7 @@ public class NotificationComparatorTest { new UserHandle(userId), "", 2000), getDefaultChannel()); mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN); - Notification n2 = new Notification.Builder(mContext) + Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .setColorized(true /* colorized */) @@ -119,7 +120,7 @@ public class NotificationComparatorTest { new UserHandle(userId), "", 1999), getDefaultChannel()); mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH); - Notification n3 = new Notification.Builder(mContext) + Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setStyle(new Notification.MediaStyle() .setMediaSession(new MediaSession.Token(null))) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) @@ -129,7 +130,7 @@ public class NotificationComparatorTest { "", 1499), getDefaultChannel()); mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); - Notification n4 = new Notification.Builder(mContext) + Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setStyle(new Notification.MessagingStyle("sender!")).build(); mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId), @@ -137,34 +138,34 @@ public class NotificationComparatorTest { mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH); mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX); - Notification n5 = new Notification.Builder(mContext) + Notification n5 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_MESSAGE).build(); mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg, smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId), "", 1299), getDefaultChannel()); mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); - Notification n6 = new Notification.Builder(mContext).build(); + Notification n6 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId), "", 1259), getDefaultChannel()); mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT); mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); - Notification n7 = new Notification.Builder(mContext).build(); + Notification n7 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId), "", 1259), getDefaultChannel()); mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT); mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); - Notification n8 = new Notification.Builder(mContext).build(); + Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId), "", 1258), getDefaultChannel()); mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH); - Notification n9 = new Notification.Builder(mContext) + Notification n9 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_MESSAGE) .setFlag(Notification.FLAG_ONGOING_EVENT |Notification.FLAG_FOREGROUND_SERVICE, true) @@ -174,7 +175,7 @@ public class NotificationComparatorTest { "", 9258), getDefaultChannel()); mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW); - Notification n10 = new Notification.Builder(mContext) + Notification n10 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build(); mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId), diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 88f1a5362008..b7b3617d7ecf 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -129,11 +129,9 @@ public class NotificationManagerServiceTest { if (channel == null) { channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT); } - Notification.Builder nb = new Notification.Builder(mContext) + Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .setChannel(channel.getId()) - .setPriority(Notification.PRIORITY_HIGH); + .setSmallIcon(android.R.drawable.sym_def_app_icon); if (extender != null) { nb.extend(extender); } 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 821007213993..62126265e117 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -93,6 +93,7 @@ public class RankingHelperTest { private final int uid = 0; private final String pkg2 = "pkg2"; private final int uid2 = 1111111; + private static final String TEST_CHANNEL_ID = "test_channel_id"; private AudioAttributes mAudioAttributes; private Context getContext() { @@ -107,7 +108,7 @@ public class RankingHelperTest { mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats, new String[]{ImportanceExtractor.class.getName()}); - mNotiGroupGSortA = new Notification.Builder(getContext()) + mNotiGroupGSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("A") .setGroup("G") .setSortKey("A") @@ -117,7 +118,7 @@ public class RankingHelperTest { "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user, null, System.currentTimeMillis()), getDefaultChannel()); - mNotiGroupGSortB = new Notification.Builder(getContext()) + mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("B") .setGroup("G") .setSortKey("B") @@ -127,7 +128,7 @@ public class RankingHelperTest { "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user, null, System.currentTimeMillis()), getDefaultChannel()); - mNotiNoGroup = new Notification.Builder(getContext()) + mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("C") .setWhen(1201) .build(); @@ -135,7 +136,7 @@ public class RankingHelperTest { "package", "package", 1, null, 0, 0, mNotiNoGroup, user, null, System.currentTimeMillis()), getDefaultChannel()); - mNotiNoGroup2 = new Notification.Builder(getContext()) + mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("D") .setWhen(1202) .build(); @@ -143,7 +144,7 @@ public class RankingHelperTest { "package", "package", 1, null, 0, 0, mNotiNoGroup2, user, null, System.currentTimeMillis()), getDefaultChannel()); - mNotiNoGroupSortA = new Notification.Builder(getContext()) + mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("E") .setWhen(1201) .setSortKey("A") diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java index c5abba8313c1..9575d3253b28 100644 --- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java @@ -51,6 +51,8 @@ import static org.mockito.Mockito.when; @SmallTest @RunWith(AndroidJUnit4.class) public class SnoozeHelperTest { + private static final String TEST_CHANNEL_ID = "test_channel_id"; + @Mock SnoozeHelper.Callback mCallback; @Mock AlarmManager mAm; @Mock ManagedServices.UserProfiles mUserProfiles; @@ -77,7 +79,7 @@ public class SnoozeHelperTest { verify(mAm, times(1)).setExactAndAllowWhileIdle( anyInt(), captor.capture(), any(PendingIntent.class)); long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime(); - assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 2); + assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 25); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); } @@ -232,20 +234,16 @@ public class SnoozeHelperTest { private NotificationRecord getNotificationRecord(String pkg, int id, String tag, UserHandle user) { - Notification n = new Notification.Builder(getContext()) + Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("A") .setGroup("G") .setSortKey("A") .setWhen(1205) .build(); + final NotificationChannel notificationChannel = new NotificationChannel( + TEST_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_LOW); return new NotificationRecord(getContext(), new StatusBarNotification( pkg, pkg, id, tag, 0, 0, n, user, null, - System.currentTimeMillis()), getDefaultChannel()); - } - - private NotificationChannel getDefaultChannel() { - return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", - NotificationManager.IMPORTANCE_LOW); + System.currentTimeMillis()), notificationChannel); } - } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index d65a9bfbf897..756514bbecc4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -34,7 +34,7 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; +import android.content.pm.StringParceledListSlice; import android.content.res.Resources; import android.graphics.Color; import android.net.IIpConnectivityMetrics; @@ -56,7 +56,6 @@ import android.util.ArraySet; import android.util.Pair; import com.android.internal.R; -import com.android.internal.util.ParcelableString; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -1220,8 +1219,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.userContexts.put(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); - ParceledListSlice<ParcelableString> oneCert = asSlice(new String[] {"1"}); - ParceledListSlice<ParcelableString> fourCerts = asSlice(new String[] {"1", "2", "3", "4"}); + StringParceledListSlice oneCert = asSlice(new String[] {"1"}); + StringParceledListSlice fourCerts = asSlice(new String[] {"1", "2", "3", "4"}); final String TEST_STRING = "Test for exactly 2 certs out of 4"; doReturn(TEST_STRING).when(mContext.resources).getQuantityText(anyInt(), eq(2)); @@ -1229,7 +1228,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Given that we have exactly one certificate installed, when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(oneCert); // when that certificate is approved, - dpms.approveCaCert(oneCert.getList().get(0).string, userId, true); + dpms.approveCaCert(oneCert.getList().get(0), userId, true); // a notification should not be shown. verify(mContext.notificationManager, timeout(1000)) .cancelAsUser(anyString(), anyInt(), eq(user)); @@ -1237,8 +1236,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Given that we have four certificates installed, when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(fourCerts); // when two of them are approved (one of them approved twice hence no action), - dpms.approveCaCert(fourCerts.getList().get(0).string, userId, true); - dpms.approveCaCert(fourCerts.getList().get(1).string, userId, true); + dpms.approveCaCert(fourCerts.getList().get(0), userId, true); + dpms.approveCaCert(fourCerts.getList().get(1), userId, true); // a notification should be shown saying that there are two certificates left to approve. verify(mContext.notificationManager, timeout(1000)) .notifyAsUser(anyString(), anyInt(), argThat( @@ -3974,18 +3973,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { } /** - * Convert String[] to ParceledListSlice<ParcelableString>. - * <p> - * TODO: This shouldn't be necessary. If ParcelableString does need to exist, it also needs - * a real constructor. + * Convert String[] to StringParceledListSlice. */ - private static ParceledListSlice<ParcelableString> asSlice(String[] s) { - List<ParcelableString> list = new ArrayList<>(s.length); - for (int i = 0; i < s.length; i++) { - ParcelableString item = new ParcelableString(); - item.string = s[i]; - list.add(i, item); - } - return new ParceledListSlice<ParcelableString>(list); + private static StringParceledListSlice asSlice(String[] s) { + return new StringParceledListSlice(Arrays.asList(s)); } } diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java index 5c46222b2feb..db7b3853d06e 100644 --- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java +++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java @@ -20,7 +20,6 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -32,6 +31,8 @@ import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.UserHandle; +import com.android.internal.notification.SystemNotificationChannels; + /** * Manager for MTP storage notification. */ @@ -77,11 +78,12 @@ class MtpNotificationManager { device.getProductName()); final String description = resources.getString( com.android.internal.R.string.usb_mtp_launch_notification_description); - final Notification.Builder builder = new Notification.Builder(mContext) - .setContentTitle(title) - .setContentText(description) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) - .setCategory(Notification.CATEGORY_SYSTEM); + final Notification.Builder builder = + new Notification.Builder(mContext, SystemNotificationChannels.USB) + .setContentTitle(title) + .setContentText(description) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) + .setCategory(Notification.CATEGORY_SYSTEM); final Intent intent = new Intent(ACTION_OPEN_IN_APPS); intent.putExtra(UsbManager.EXTRA_DEVICE, device); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 07b4ca12171f..b1df0af100af 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -50,6 +50,7 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.IndentingPrintWriter; import com.android.server.FgThread; @@ -912,13 +913,13 @@ public class UsbDeviceManager { PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); - Notification notification = new Notification.Builder(mContext) + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.USB) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) .setWhen(0) .setOngoing(true) .setTicker(title) .setDefaults(0) // please be quiet - .setPriority(Notification.PRIORITY_MIN) .setColor(mContext.getColor( com.android.internal.R.color.system_notification_accent_color)) .setContentTitle(title) @@ -951,13 +952,13 @@ public class UsbDeviceManager { PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); - Notification notification = new Notification.Builder(mContext) + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) .setWhen(0) .setOngoing(true) .setTicker(title) .setDefaults(0) // please be quiet - .setPriority(Notification.PRIORITY_DEFAULT) .setColor(mContext.getColor( com.android.internal.R.color.system_notification_accent_color)) .setContentTitle(title) diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 4295d401edd4..3f7c908436a4 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -878,6 +878,7 @@ public final class Call { * party, if it is active. */ public static final class RttCall { + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({RTT_MODE_INVALID, RTT_MODE_FULL, RTT_MODE_HCO, RTT_MODE_VCO}) public @interface RttAudioMode {} diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 6807ef4b0601..2e144f24bfa9 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1130,18 +1130,22 @@ public class TelecomManager { /** * If there is a ringing incoming call, this method accepts the call on behalf of the user. - * TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use - * this method (clockwork & gearhead). + * * If the incoming call is a video call, the call will be answered with the same video state as * the incoming call requests. This means, for example, that an incoming call requesting * {@link VideoProfile#STATE_BIDIRECTIONAL} will be answered, accepting that state. - * @hide + * + * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or + * {@link android.Manifest.permission#ANSWER_PHONE_CALLS} */ - @SystemApi + //TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use + // this method (clockwork & gearhead). + @RequiresPermission(anyOf = + {Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall() { try { if (isServiceConnected()) { - getTelecomService().acceptRingingCall(); + getTelecomService().acceptRingingCall(mContext.getPackageName()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#acceptRingingCall", e); @@ -1152,14 +1156,18 @@ public class TelecomManager { * If there is a ringing incoming call, this method accepts the call on behalf of the user, * with the specified video state. * + * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or + * {@link android.Manifest.permission#ANSWER_PHONE_CALLS} + * * @param videoState The desired video state to answer the call with. - * @hide */ - @SystemApi + @RequiresPermission(anyOf = + {Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int videoState) { try { if (isServiceConnected()) { - getTelecomService().acceptRingingCallWithVideoState(videoState); + getTelecomService().acceptRingingCallWithVideoState( + mContext.getPackageName(), videoState); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#acceptRingingCallWithVideoState", e); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index d9465dce49d2..eb1cde393cd4 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -182,12 +182,12 @@ interface ITelecomService { /** * @see TelecomServiceImpl#acceptRingingCall */ - void acceptRingingCall(); + void acceptRingingCall(String callingPackage); /** * @see TelecomServiceImpl#acceptRingingCallWithVideoState(int) */ - void acceptRingingCallWithVideoState(int videoState); + void acceptRingingCallWithVideoState(String callingPackage, int videoState); /** * @see TelecomServiceImpl#cancelMissedCallsNotification diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 406f01e82c35..f1f683c70735 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -18,6 +18,7 @@ package android.telephony.ims; import android.app.PendingIntent; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; @@ -43,6 +44,7 @@ import com.android.internal.annotations.VisibleForTesting; import static android.Manifest.permission.MODIFY_PHONE_STATE; import static android.Manifest.permission.READ_PHONE_STATE; +import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend @@ -137,7 +139,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public boolean isConnected(int slotId, int featureType, int callSessionType, int callType) throws RemoteException { - enforceCallingOrSelfPermission(READ_PHONE_STATE, "isConnected"); + enforceReadPhoneStatePermission("isConnected"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -149,7 +151,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public boolean isOpened(int slotId, int featureType) throws RemoteException { - enforceCallingOrSelfPermission(READ_PHONE_STATE, "isOpened"); + enforceReadPhoneStatePermission("isOpened"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -161,7 +163,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public int getFeatureStatus(int slotId, int featureType) throws RemoteException { - enforceCallingOrSelfPermission(READ_PHONE_STATE, "getFeatureStatus"); + enforceReadPhoneStatePermission("getFeatureStatus"); int status = ImsFeature.STATE_NOT_AVAILABLE; synchronized (mFeatures) { SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); @@ -178,7 +180,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void addRegistrationListener(int slotId, int featureType, IImsRegistrationListener listener) throws RemoteException { - enforceCallingOrSelfPermission(READ_PHONE_STATE, "addRegistrationListener"); + enforceReadPhoneStatePermission("addRegistrationListener"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -190,7 +192,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void removeRegistrationListener(int slotId, int featureType, IImsRegistrationListener listener) throws RemoteException { - enforceCallingOrSelfPermission(READ_PHONE_STATE, "removeRegistrationListener"); + enforceReadPhoneStatePermission("removeRegistrationListener"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -351,6 +353,8 @@ public abstract class ImsService extends ImsServiceBase { } ImsFeature f = makeImsFeature(slotId, featureType); if (f != null) { + f.setContext(this); + f.setSlotId(slotId); f.setImsFeatureStatusCallback(c); featureMap.put(featureType, f); } @@ -434,6 +438,17 @@ public abstract class ImsService extends ImsServiceBase { } /** + * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a + * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps. + */ + private void enforceReadPhoneStatePermission(String fn) { + if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + enforceCallingOrSelfPermission(READ_PHONE_STATE, fn); + } + } + + /** * @return An implementation of MMTelFeature that will be used by the system for MMTel * functionality. Must be able to handle emergency calls at any time as well. */ diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 8d7d260104e2..988dd588ecad 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -17,7 +17,10 @@ package android.telephony.ims.feature; import android.annotation.IntDef; +import android.content.Context; +import android.content.Intent; import android.os.RemoteException; +import android.telephony.SubscriptionManager; import android.util.Log; import com.android.ims.internal.IImsFeatureStatusCallback; @@ -35,6 +38,32 @@ public abstract class ImsFeature { private static final String LOG_TAG = "ImsFeature"; + /** + * Action to broadcast when ImsService is up. + * Internal use only. + * Only defined here separately compatibility purposes with the old ImsService. + * @hide + */ + public static final String ACTION_IMS_SERVICE_UP = + "com.android.ims.IMS_SERVICE_UP"; + + /** + * Action to broadcast when ImsService is down. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * @hide + */ + public static final String ACTION_IMS_SERVICE_DOWN = + "com.android.ims.IMS_SERVICE_DOWN"; + + /** + * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. + * A long value; the phone ID corresponding to the IMS service coming up or down. + * Only defined here separately for compatibility purposes with the old ImsService. + * @hide + */ + public static final String EXTRA_PHONE_ID = "android:phone_id"; + // Invalid feature value public static final int INVALID = -1; // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously @@ -61,11 +90,21 @@ public abstract class ImsFeature { private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>(); private IImsFeatureStatusCallback mStatusCallback; private @ImsState int mState = STATE_NOT_AVAILABLE; + private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + private Context mContext; public interface INotifyFeatureRemoved { void onFeatureRemoved(int slotId); } + public void setContext(Context context) { + mContext = context; + } + + public void setSlotId(int slotId) { + mSlotId = slotId; + } + public void addFeatureRemovedListener(INotifyFeatureRemoved listener) { synchronized (mRemovedListeners) { mRemovedListeners.add(listener); @@ -118,6 +157,30 @@ public abstract class ImsFeature { Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); } } + sendImsServiceIntent(state); + } + + /** + * Provide backwards compatibility using deprecated service UP/DOWN intents. + */ + private void sendImsServiceIntent(@ImsState int state) { + if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + return; + } + Intent intent; + switch (state) { + case ImsFeature.STATE_NOT_AVAILABLE: + case ImsFeature.STATE_INITIALIZING: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + break; + case ImsFeature.STATE_READY: + intent = new Intent(ACTION_IMS_SERVICE_UP); + break; + default: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + } + intent.putExtra(EXTRA_PHONE_ID, mSlotId); + mContext.sendBroadcast(intent); } /** diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index eeaf26f92f47..c05045ebd18d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -369,6 +369,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { connect(false); } + public void suspend() { + mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + public void disconnect() { mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); mNetworkAgent.sendNetworkInfo(mNetworkInfo); @@ -1054,6 +1059,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { AVAILABLE, NETWORK_CAPABILITIES, LINK_PROPERTIES, + SUSPENDED, LOSING, LOST, UNAVAILABLE @@ -1067,7 +1073,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { state = s; network = n; arg = o; } public String toString() { - return String.format("%s (%s)", state, network); + return String.format("%s (%s) (%s)", state, network, arg); } @Override public boolean equals(Object o) { @@ -1105,11 +1111,26 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) { + setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap); + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) { + setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp); + } + + @Override public void onUnavailable() { setLastCallback(CallbackState.UNAVAILABLE, null, null); } @Override + public void onNetworkSuspended(Network network) { + setLastCallback(CallbackState.SUSPENDED, network, null); + } + + @Override public void onLosing(Network network, int maxMsToLive) { setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */); } @@ -1132,11 +1153,12 @@ public class ConnectivityServiceTest extends AndroidTestCase { return cb; } - void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) { - CallbackInfo expected = new CallbackInfo( - state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0); + CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs) { + final Network expectedNetwork = (agent != null) ? agent.getNetwork() : null; + CallbackInfo expected = new CallbackInfo(state, expectedNetwork, 0); CallbackInfo actual = nextCallback(timeoutMs); assertEquals("Unexpected callback:", expected, actual); + if (state == CallbackState.LOSING) { String msg = String.format( "Invalid linger time value %d, must be between %d and %d", @@ -1144,10 +1166,50 @@ public class ConnectivityServiceTest extends AndroidTestCase { int maxMsToLive = (Integer) actual.arg; assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS); } + + return actual; } - void expectCallback(CallbackState state, MockNetworkAgent mockAgent) { - expectCallback(state, mockAgent, TIMEOUT_MS); + CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) { + return expectCallback(state, agent, TIMEOUT_MS); + } + + void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) { + expectCallback(CallbackState.AVAILABLE, agent, timeoutMs); + + final boolean HAS_DATASYNC_ON_AVAILABLE = false; + if (HAS_DATASYNC_ON_AVAILABLE) { + if (expectSuspended) { + expectCallback(CallbackState.SUSPENDED, agent, timeoutMs); + } + expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs); + expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs); + } + } + + void expectAvailableCallbacks(MockNetworkAgent agent) { + expectAvailableCallbacks(agent, false, TIMEOUT_MS); + } + + void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) { + expectAvailableCallbacks(agent, true, TIMEOUT_MS); + } + + void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) { + expectAvailableCallbacks(agent, true, TIMEOUT_MS); + expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent); + } + + void expectCapabilitiesWith(int capability, MockNetworkAgent agent) { + CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent); + NetworkCapabilities nc = (NetworkCapabilities) cbi.arg; + assertTrue(nc.hasCapability(capability)); + } + + void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) { + CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent); + NetworkCapabilities nc = (NetworkCapabilities) cbi.arg; + assertFalse(nc.hasCapability(capability)); } void assertNoCallback() { @@ -1184,8 +1246,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); - genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent); + cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); @@ -1199,8 +1261,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); @@ -1223,8 +1285,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Test validated networks mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); @@ -1236,9 +1298,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent); genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); - wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); @@ -1274,28 +1337,32 @@ public class ConnectivityServiceTest extends AndroidTestCase { mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mCellNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mWiFiNetworkAgent.connect(true); // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request. // We then get LOSING when wifi validates and cell is outscored. - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mEthernetNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent); + callback.expectAvailableCallbacks(mEthernetNetworkAgent); + // TODO: Investigate sending validated before losing. callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent); assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mEthernetNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); for (int i = 0; i < 4; i++) { MockNetworkAgent oldNetwork, newNetwork; @@ -1312,7 +1379,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { callback.expectCallback(CallbackState.LOSING, oldNetwork); // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no // longer lingering? - defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork); + defaultCallback.expectAvailableCallbacks(newNetwork); assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork()); } assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); @@ -1320,17 +1387,19 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even // if the network is still up. mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + // We expect a notification about the capabilities change, and nothing else. + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); + defaultCallback.assertNoCallback(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Wifi no longer satisfies our listen, which is for an unmetered network. // But because its score is 55, it's still up (and the default network). - defaultCallback.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); // Disconnect our test networks. mWiFiNetworkAgent.disconnect(); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); mCellNetworkAgent.disconnect(); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); @@ -1346,22 +1415,22 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); // Score: 10 - callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + callback.expectAvailableCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); // Bring up wifi with a score of 20. // Cell stays up because it would satisfy the default request if it validated. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); // Score: 20 - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); // Bring up wifi with a score of 70. @@ -1369,31 +1438,33 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.adjustScore(50); mWiFiNetworkAgent.connect(false); // Score: 70 - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); // Tear down wifi. mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but // it's arguably correct to linger it, since it was the default network before it validated. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); mCellNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mCellNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); @@ -1401,13 +1472,15 @@ public class ConnectivityServiceTest extends AndroidTestCase { // If a network is lingering, and we add and remove a request from it, resume lingering. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); NetworkRequest cellRequest = new NetworkRequest.Builder() .addTransportType(TRANSPORT_CELLULAR).build(); @@ -1423,7 +1496,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); // Cell is now the default network. Pin it with a cell-specific request. noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525 @@ -1432,8 +1505,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Now connect wifi, and expect it to become the default network. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); // The default request is lingering on cell, but nothing happens to cell, and we send no // callbacks for it, because it's kept up by cellRequest. callback.assertNoCallback(); @@ -1619,7 +1692,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); mCellNetworkAgent.connectWithoutInternet(); - networkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + networkCallback.expectAvailableCallbacks(mCellNetworkAgent); verifyActiveNetwork(TRANSPORT_WIFI); // Test releasing NetworkRequest disconnects cellular with MMS cv = mCellNetworkAgent.getDisconnectedCV(); @@ -1645,7 +1718,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); mmsNetworkAgent.connectWithoutInternet(); - networkCallback.expectCallback(CallbackState.AVAILABLE, mmsNetworkAgent); + networkCallback.expectAvailableCallbacks(mmsNetworkAgent); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent cv = mmsNetworkAgent.getDisconnectedCV(); @@ -1671,7 +1744,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); - captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); // Take down network. @@ -1684,7 +1757,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); - captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl); // Make captive portal disappear then revalidate. @@ -1694,7 +1767,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent); + // TODO: Investigate only sending available callbacks. + validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. @@ -1739,7 +1814,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent); // But there should be no CaptivePortal callback. captivePortalCallback.assertNoCallback(); } @@ -1792,14 +1867,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Bring up cell and expect CALLBACK_AVAILABLE. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); // Bring up wifi and expect CALLBACK_AVAILABLE. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); cellNetworkCallback.assertNoCallback(); - defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); // Bring down cell. Expect no default network callback, since it wasn't the default. mCellNetworkAgent.disconnect(); @@ -1809,7 +1884,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Bring up cell. Expect no default network callback, since it won't be the default. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); // Bring down wifi. Expect the default network callback to notified of LOST wifi @@ -1817,28 +1892,16 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.disconnect(); cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent); mCellNetworkAgent.disconnect(); cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); } - private class TestRequestUpdateCallback extends TestNetworkCallback { - @Override - public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) { - setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap); - } - - @Override - public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) { - setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp); - } - } - @SmallTest - public void testRequestCallbackUpdates() throws Exception { + public void testAdditionalStateCallbacks() throws Exception { // File a network request for mobile. - final TestNetworkCallback cellNetworkCallback = new TestRequestUpdateCallback(); + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); final NetworkRequest cellRequest = new NetworkRequest.Builder() .addTransportType(TRANSPORT_CELLULAR).build(); mCm.requestNetwork(cellRequest, cellNetworkCallback); @@ -1847,10 +1910,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - // We should get onAvailable(). - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - // We should get onCapabilitiesChanged(), when the mobile network successfully validates. - cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent); + // We should get onAvailable(), onCapabilitiesChanged(), and + // onLinkPropertiesChanged() in rapid succession. Additionally, we + // should get onCapabilitiesChanged() when the mobile network validates. + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); // Update LinkProperties. @@ -1861,20 +1924,28 @@ public class ConnectivityServiceTest extends AndroidTestCase { cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); + // Suspend the network. + mCellNetworkAgent.suspend(); + cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + // Register a garden variety default network request. - final TestNetworkCallback dfltNetworkCallback = new TestRequestUpdateCallback(); + final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(dfltNetworkCallback); - // Only onAvailable() is called; no other information is delivered. - dfltNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(), + // as well as onNetworkSuspended() in rapid succession. + dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent); dfltNetworkCallback.assertNoCallback(); // Request a NetworkCapabilities update; only the requesting callback is notified. + // TODO: Delete this together with Connectivity{Manager,Service} code. mCm.requestNetworkCapabilities(dfltNetworkCallback); dfltNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); dfltNetworkCallback.assertNoCallback(); // Request a LinkProperties update; only the requesting callback is notified. + // TODO: Delete this together with Connectivity{Manager,Service} code. mCm.requestLinkProperties(dfltNetworkCallback); dfltNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); @@ -1917,18 +1988,20 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); assertTrue(isForegroundNetwork(mCellNetworkAgent)); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); // When wifi connects, cell lingers. - callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - fgCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectAvailableCallbacks(mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent); fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); assertTrue(isForegroundNetwork(mCellNetworkAgent)); assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); @@ -1936,7 +2009,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { mService.waitForIdle(); int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs); - callback.assertNoCallback(); + // Expect a network capabilities update sans FOREGROUND. + callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); assertFalse(isForegroundNetwork(mCellNetworkAgent)); assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); @@ -1945,9 +2019,15 @@ public class ConnectivityServiceTest extends AndroidTestCase { .addTransportType(TRANSPORT_CELLULAR).build(); final TestNetworkCallback cellCallback = new TestNetworkCallback(); mCm.requestNetwork(cellRequest, cellCallback); - cellCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - callback.assertNoCallback(); // Because the network is already up. + // NOTE: This request causes the network's capabilities to change. This + // is currently delivered before the onAvailable() callbacks. + // TODO: Fix this. + cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); + cellCallback.expectAvailableCallbacks(mCellNetworkAgent); + fgCallback.expectAvailableCallbacks(mCellNetworkAgent); + // Expect a network capabilities update with FOREGROUND, because the most recent + // request causes its state to change. + callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); assertTrue(isForegroundNetwork(mCellNetworkAgent)); assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); @@ -1955,7 +2035,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { // lingering. mCm.unregisterNetworkCallback(cellCallback); fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); - callback.assertNoCallback(); + // Expect a network capabilities update sans FOREGROUND. + callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); assertFalse(isForegroundNetwork(mCellNetworkAgent)); assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); @@ -1963,7 +2044,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + fgCallback.expectAvailableCallbacks(mCellNetworkAgent); assertTrue(isForegroundNetwork(mCellNetworkAgent)); mCm.unregisterNetworkCallback(callback); @@ -2104,7 +2185,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); testFactory.expectAddRequests(2); // Because the cell request changes score twice. mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); testFactory.waitForNetworkRequests(2); assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us. @@ -2195,20 +2276,22 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Bring up validated cell. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); Network cellNetwork = mCellNetworkAgent.getNetwork(); // Bring up validated wifi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi. mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; mCm.reportNetworkConnectivity(wifiNetwork, false); + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Because avoid bad wifi is off, we don't switch to cellular. @@ -2223,18 +2306,18 @@ public class ConnectivityServiceTest extends AndroidTestCase { // that we switch back to cell. tracker.configRestrictsAvoidBadWifi = false; tracker.reevaluate(); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCm.getActiveNetwork(), cellNetwork); // Switch back to a restrictive carrier. tracker.configRestrictsAvoidBadWifi = true; tracker.reevaluate(); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mCm.getActiveNetwork(), wifiNetwork); // Simulate the user selecting "switch" on the dialog, and check that we switch to cell. mCm.setAvoidUnvalidated(wifiNetwork); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( NET_CAPABILITY_VALIDATED)); assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( @@ -2245,13 +2328,15 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); - validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi and expect the dialog to appear. mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; mCm.reportNetworkConnectivity(wifiNetwork, false); + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Simulate the user selecting "switch" and checking the don't ask again checkbox. @@ -2259,7 +2344,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { tracker.reevaluate(); // We now switch to cell. - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( NET_CAPABILITY_VALIDATED)); assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( @@ -2270,17 +2355,17 @@ public class ConnectivityServiceTest extends AndroidTestCase { // We switch to wifi and then to cell. Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null); tracker.reevaluate(); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); assertEquals(mCm.getActiveNetwork(), wifiNetwork); Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); tracker.reevaluate(); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacks(mCellNetworkAgent); assertEquals(mCm.getActiveNetwork(), cellNetwork); // If cell goes down, we switch to wifi. mCellNetworkAgent.disconnect(); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent); validatedWifiCallback.assertNoCallback(); mCm.unregisterNetworkCallback(cellNetworkCallback); @@ -2322,7 +2407,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, timeoutMs); + networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs); // pass timeout and validate that UNAVAILABLE is not called networkCallback.assertNoCallback(); @@ -2343,7 +2428,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); final int assertTimeoutMs = 150; - networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, assertTimeoutMs); + networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs); sleepFor(20); mWiFiNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index df3ce19b05e5..04fdae9f2db4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -106,7 +106,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; +import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1; import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE; /** diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java index 5386b1758682..7f8d9928d7ce 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java @@ -16,13 +16,13 @@ package com.android.layoutlib.bridge.bars; -import android.os.Build.VERSION_CODES; +import android.os._Original_Build.VERSION_CODES; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static android.os.Build.VERSION_CODES.*; +import static android.os._Original_Build.VERSION_CODES.*; /** * Various helper methods to simulate older versions of platform. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index a6e5fb841bff..8bb2c593f7a1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -49,7 +49,7 @@ import android.widget.TextView; import java.io.IOException; import java.io.InputStream; -import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.os._Original_Build.VERSION_CODES.LOLLIPOP; /** * Base "bar" class for the window decor around the the edited layout. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index bed5806aadad..d59b41980179 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -37,6 +37,7 @@ import java.util.TreeMap; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Class that generates a new JAR from a list of classes, some of which are to be kept as-is @@ -127,7 +128,10 @@ public class AsmGenerator { // Create the map of classes to rename. mRenameClasses = new HashMap<>(); mClassesNotRenamed = new HashSet<>(); - String[] renameClasses = createInfo.getRenamedClasses(); + String[] renameClasses = Stream.concat( + Arrays.stream(createInfo.getRenamedClasses()), + Arrays.stream(createInfo.getRefactoredClasses())) + .toArray(String[]::new); int n = renameClasses.length; for (int i = 0; i < n; i += 2) { assert i + 1 < n; @@ -140,7 +144,10 @@ public class AsmGenerator { // Create a map of classes to be refactored. mRefactorClasses = new HashMap<>(); - String[] refactorClasses = createInfo.getJavaPkgClasses(); + String[] refactorClasses = Stream.concat( + Arrays.stream(createInfo.getJavaPkgClasses()), + Arrays.stream(createInfo.getRefactoredClasses())) + .toArray(String[]::new); n = refactorClasses.length; for (int i = 0; i < n; i += 2) { assert i + 1 < n; diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index a8582c60bc64..b0aa3c2989a5 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -36,66 +36,42 @@ import java.util.Set; */ public final class CreateInfo implements ICreateInfo { - /** - * Returns the list of class from layoutlib_create to inject in layoutlib. - * The list can be empty but must not be null. - */ @Override public Class<?>[] getInjectedClasses() { return INJECTED_CLASSES; } - /** - * Returns the list of methods to rewrite as delegates. - * The list can be empty but must not be null. - */ @Override public String[] getDelegateMethods() { return DELEGATE_METHODS; } - /** - * Returns the list of classes on which to delegate all native methods. - * The list can be empty but must not be null. - */ @Override public String[] getDelegateClassNatives() { return DELEGATE_CLASS_NATIVES; } - /** - * Returns the list of classes to rename, must be an even list: the binary FQCN - * of class to replace followed by the new FQCN. - * The list can be empty but must not be null. - */ @Override public String[] getRenamedClasses() { return RENAMED_CLASSES; } - /** - * Returns the list of classes for which the methods returning them should be deleted. - * The array contains a list of null terminated section starting with the name of the class - * to rename in which the methods are deleted, followed by a list of return types identifying - * the methods to delete. - * The list can be empty but must not be null. - */ @Override public String[] getDeleteReturns() { return DELETE_RETURNS; } - /** - * Returns the list of classes to refactor, must be an even list: the binary FQCN of class to - * replace followed by the new FQCN. All references to the old class should be updated to the - * new class. The list can be empty but must not be null. - */ @Override public String[] getJavaPkgClasses() { return JAVA_PKG_CLASSES; } @Override + public String[] getRefactoredClasses() { + return REFACTOR_CLASSES; + } + + @Override public Set<String> getExcludedClasses() { String[] refactoredClasses = getJavaPkgClasses(); int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length; @@ -333,10 +309,22 @@ public final class CreateInfo implements ICreateInfo { "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat", }; + /** + * List of classes to refactor. This is similar to combining {@link #getRenamedClasses()} and + * {@link #getJavaPkgClasses()}. + * Classes included here will be renamed and then all their references in any other classes + * will be also modified. + * FQCN of class to refactor followed by its new FQCN. + */ + private final static String[] REFACTOR_CLASSES = + new String[] { + "android.os.Build", "android.os._Original_Build", + }; + private final static String[] EXCLUDED_CLASSES = new String[] { "android.preference.PreferenceActivity", - "org.kxml2.io.KXmlParser" + "org.kxml2.io.KXmlParser", }; /** diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java index 48abde4517e6..eca1c078faec 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java @@ -52,6 +52,15 @@ public interface ICreateInfo { String[] getRenamedClasses(); /** + * List of classes to refactor. This is similar to combining {@link #getRenamedClasses()} and + * {@link #getJavaPkgClasses()}. + * Classes included here will be renamed and then all their references in any other classes + * will be also modified. + * FQCN of class to refactor followed by its new FQCN. + */ + String[] getRefactoredClasses(); + + /** * Returns the list of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class * to rename in which the methods are deleted, followed by a list of return types identifying diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 4d5d5d2c4a6e..e718fb9133ee 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -35,6 +35,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -54,8 +55,6 @@ import static org.junit.Assert.assertTrue; * Unit tests for some methods of {@link AsmGenerator}. */ public class AsmGeneratorTest { - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockLog mLog; private ArrayList<String> mOsJarPath; private String mOsDestJar; @@ -81,6 +80,7 @@ public class AsmGeneratorTest { @After public void tearDown() throws Exception { if (mTempFile != null) { + //noinspection ResultOfMethodCallIgnored mTempFile.delete(); mTempFile = null; } @@ -89,23 +89,7 @@ public class AsmGeneratorTest { @Test public void testClassRenaming() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { - @Override - public Class<?>[] getInjectedClasses() { - // classes to inject in the final JAR - return new Class<?>[0]; - } - - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - + ICreateInfo ci = new CreateInfoAdapter() { @Override public String[] getRenamedClasses() { // classes to rename (so that we can replace them) @@ -114,37 +98,6 @@ public class AsmGeneratorTest { "not.an.actual.ClassName", "anoter.fake.NewClassName", }; } - - @Override - public String[] getJavaPkgClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Set<String> getExcludedClasses() { - return null; - } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -154,7 +107,7 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - Collections.<String>emptySet() /* excluded classes */, + Collections.emptySet() /* excluded classes */, new String[]{} /* include files */); aa.analyze(); agen.generate(); @@ -165,8 +118,8 @@ public class AsmGeneratorTest { } @Test - public void testClassRefactoring() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { + public void testJavaClassRefactoring() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Class<?>[] getInjectedClasses() { // classes to inject in the final JAR @@ -176,22 +129,6 @@ public class AsmGeneratorTest { } @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override public String[] getJavaPkgClasses() { // classes to refactor (so that we can replace them) return new String[] { @@ -203,27 +140,6 @@ public class AsmGeneratorTest { public Set<String> getExcludedClasses() { return Collections.singleton("java.lang.JavaClass"); } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -233,7 +149,7 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - Collections.<String>emptySet(), + Collections.emptySet(), new String[] { /* include files */ "mock_android/data/data*" }); @@ -242,47 +158,64 @@ public class AsmGeneratorTest { Map<String, ClassReader> output = new TreeMap<>(); Map<String, InputStream> filesFound = new TreeMap<>(); parseZip(mOsDestJar, output, filesFound); - boolean injectedClassFound = false; + RecordingClassVisitor cv = new RecordingClassVisitor(); for (ClassReader cr: output.values()) { - TestClassVisitor cv = new TestClassVisitor(); cr.accept(cv, 0); - injectedClassFound |= cv.mInjectedClassFound; } - assertTrue(injectedClassFound); + assertTrue(cv.mVisitedClasses.contains( + "com/android/tools/layoutlib/create/dataclass/JavaClass")); + assertFalse(cv.mVisitedClasses.contains( + JAVA_CLASS_NAME)); assertArrayEquals(new String[] {"mock_android/data/dataFile"}, filesFound.keySet().toArray()); } @Test - public void testClassExclusion() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { + public void testClassRefactoring() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Class<?>[] getInjectedClasses() { - return new Class<?>[0]; - } - - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; + // classes to inject in the final JAR + return new Class<?>[] { + com.android.tools.layoutlib.create.dataclass.JavaClass.class + }; } @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; + public String[] getRefactoredClasses() { + // classes to refactor (so that we can replace them) + return new String[] { + "mock_android.view.View", "mock_android.view._Original_View", + }; } + }; - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } + AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); - @Override - public String[] getJavaPkgClasses() { - // classes to refactor (so that we can replace them) - return EMPTY_STRING_ARRAY; - } + AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen, + null, // derived from + new String[] { // include classes + "**" + }, + Collections.emptySet(), + new String[] {}); + aa.analyze(); + agen.generate(); + Map<String, ClassReader> output = new TreeMap<>(); + parseZip(mOsDestJar, output, new TreeMap<>()); + RecordingClassVisitor cv = new RecordingClassVisitor(); + for (ClassReader cr: output.values()) { + cr.accept(cv, 0); + } + assertTrue(cv.mVisitedClasses.contains( + "mock_android/view/_Original_View")); + assertFalse(cv.mVisitedClasses.contains( + "mock_android/view/View")); + } + @Test + public void testClassExclusion() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Set<String> getExcludedClasses() { Set<String> set = new HashSet<>(2); @@ -290,27 +223,6 @@ public class AsmGeneratorTest { set.add("java.lang.JavaClass"); return set; } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -340,55 +252,7 @@ public class AsmGeneratorTest { public void testMethodInjection() throws IOException, LogAbortException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { - ICreateInfo ci = new ICreateInfo() { - @Override - public Class<?>[] getInjectedClasses() { - return new Class<?>[0]; - } - - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getJavaPkgClasses() { - // classes to refactor (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override - public Set<String> getExcludedClasses() { - return Collections.emptySet(); - } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - + ICreateInfo ci = new CreateInfoAdapter() { @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return Collections.singletonMap("mock_android.util.EmptyArray", @@ -474,97 +338,94 @@ public class AsmGeneratorTest { } } - private class TestClassVisitor extends ClassVisitor { + /** + * {@link ClassVisitor} that records every class that sees. + */ + private static class RecordingClassVisitor extends ClassVisitor { + private Set<String> mVisitedClasses = new HashSet<>(); - boolean mInjectedClassFound = false; - - TestClassVisitor() { + private RecordingClassVisitor() { super(Main.ASM_VERSION); } + private void addClass(String className) { + if (className == null) { + return; + } + + int pos = className.indexOf('$'); + if (pos > 0) { + // For inner classes, add also the base class + mVisitedClasses.add(className.substring(0, pos)); + } + mVisitedClasses.add(className); + } + @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - assertTrue(!getBase(name).equals(JAVA_CLASS_NAME)); - if (name.equals("com/android/tools/layoutlib/create/dataclass/JavaClass")) { - mInjectedClassFound = true; + public void visit(int version, int access, String name, String signature, String superName, + String[] interfaces) { + addClass(superName); + Arrays.stream(interfaces).forEach(this::addClass); + } + + private void processType(Type type) { + switch (type.getSort()) { + case Type.OBJECT: + addClass(type.getInternalName()); + break; + case Type.ARRAY: + addClass(type.getElementType().getInternalName()); + break; + case Type.METHOD: + processType(type.getReturnType()); + Arrays.stream(type.getArgumentTypes()).forEach(this::processType); + break; } - super.visit(version, access, name, signature, superName, interfaces); } @Override - public FieldVisitor visitField(int access, String name, String desc, - String signature, Object value) { - assertTrue(testType(Type.getType(desc))); + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + processType(Type.getType(desc)); return super.visitField(access, name, desc, signature, value); } - @SuppressWarnings("hiding") @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(Main.ASM_VERSION, mv) { @Override - public void visitFieldInsn(int opcode, String owner, String name, - String desc) { - assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME)); - assertTrue(testType(Type.getType(desc))); + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + addClass(owner); + processType(Type.getType(desc)); super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitLdcInsn(Object cst) { if (cst instanceof Type) { - assertTrue(testType((Type)cst)); + processType((Type) cst); } super.visitLdcInsn(cst); } @Override public void visitTypeInsn(int opcode, String type) { - assertTrue(!getBase(type).equals(JAVA_CLASS_NAME)); + addClass(type); super.visitTypeInsn(opcode, type); } @Override - public void visitMethodInsn(int opcode, String owner, String name, - String desc, boolean itf) { - assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME)); - assertTrue(testType(Type.getType(desc))); + public void visitMethodInsn(int opcode, String owner, String name, String desc, + boolean itf) { + addClass(owner); + processType(Type.getType(desc)); super.visitMethodInsn(opcode, owner, name, desc, itf); } }; } - - private boolean testType(Type type) { - int sort = type.getSort(); - if (sort == Type.OBJECT) { - assertTrue(!getBase(type.getInternalName()).equals(JAVA_CLASS_NAME)); - } else if (sort == Type.ARRAY) { - assertTrue(!getBase(type.getElementType().getInternalName()) - .equals(JAVA_CLASS_NAME)); - } else if (sort == Type.METHOD) { - boolean r = true; - for (Type t : type.getArgumentTypes()) { - r &= testType(t); - } - return r & testType(type.getReturnType()); - } - return true; - } - - private String getBase(String className) { - if (className == null) { - return null; - } - int pos = className.indexOf('$'); - if (pos > 0) { - return className.substring(0, pos); - } - return className; - } } } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java new file mode 100644 index 000000000000..ad7cb9a0ed40 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java @@ -0,0 +1,80 @@ +/* + * 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.tools.layoutlib.create; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +class CreateInfoAdapter implements ICreateInfo { + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + @Override + public Class<?>[] getInjectedClasses() { + return new Class<?>[0]; + } + + @Override + public String[] getDelegateMethods() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getDelegateClassNatives() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getRenamedClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getRefactoredClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getDeleteReturns() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getJavaPkgClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public Set<String> getExcludedClasses() { + return Collections.emptySet(); + } + + @Override + public String[] getPromotedFields() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getPromotedClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { + return Collections.emptyMap(); + } +} diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index 57b98e984f17..59fe1ee6bc80 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -31,7 +31,7 @@ import java.lang.ref.WeakReference; * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This * class provides functionality common to both publish and subscribe discovery sessions: * <ul> - * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])}. + * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method. * <li>Creating a network-specifier when requesting a Aware connection: * {@link #createNetworkSpecifier(PeerHandle, byte[])}. * </ul> @@ -247,8 +247,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a - * WiFi Aware connection to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an + * unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -256,7 +256,58 @@ public class DiscoverySession { * discovery or communication (in such scenarios the MAC address of the peer is shielded by * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other * OOB (out-of-band) mechanism then use the alternative - * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the + * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the + * peer's MAC address. + * <p> + * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR + * and a Publisher is a RESPONDER. + * + * @param peerHandle The peer's handle obtained through + * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} + * or + * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}. + * On a RESPONDER this value is used to gate the acceptance of a connection + * request from only that peer. A RESPONDER may specify a null - indicating + * that it will accept connection requests from any device. + * + * @return A string to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, + * android.net.ConnectivityManager.NetworkCallback)} + * [or other varieties of that API]. + * + * @hide + */ + public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) { + if (mTerminated) { + Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session"); + return null; + } else { + WifiAwareManager mgr = mMgr.get(); + if (mgr == null) { + Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); + return null; + } + + int role = this instanceof SubscribeDiscoverySession + ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR + : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; + + return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null); + } + } + + /** + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an + * encrypted WiFi Aware connection (link) to the specified peer. The + * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to + * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. + * <p> + * This method should be used when setting up a connection with a peer discovered through Aware + * discovery or communication (in such scenarios the MAC address of the peer is shielded by + * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other + * OOB (out-of-band) mechanism then use the alternative + * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses the * peer's MAC address. * <p> * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR @@ -267,29 +318,34 @@ public class DiscoverySession { * byte[], java.util.List)} or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request - * from only that peer. A RESPONDER may specified a null - indicating that + * from only that peer. A RESPONDER may specify a null - indicating that * it will accept connection requests from any device. - * @param token An arbitrary token (message) to be used to match connection initiation request - * to a responder setup. A RESPONDER is set up with a {@code token} which must - * be matched by the token provided by the INITIATOR. A null token is permitted - * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is - * not the same as a null token and requires the peer token to be empty as well. + * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for + * encrypting the data-path. Use the + * {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an open (unencrypted) + * link. * * @return A string to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. + * + * @hide */ - public String createNetworkSpecifier(@Nullable PeerHandle peerHandle, - @Nullable byte[] token) { + public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, + @NonNull byte[] pmk) { + if (pmk == null || pmk.length == 0) { + throw new IllegalArgumentException("PMK must not be null or empty"); + } + if (mTerminated) { - Log.w(TAG, "createNetworkSpecifier: called on terminated session"); + Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session"); return null; } else { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { - Log.w(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager"); + Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); return null; } @@ -297,7 +353,30 @@ public class DiscoverySession { ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; - return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token); + return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk); } } + + /** + * Place-holder for {@code createNetworkSpecifierOpen(PeerHandle)}. Present to enable + * development of replacements CL without causing an API change. Will be removed when new + * APIs are exposed. + * + * @param peerHandle The peer's handle obtained through + * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, + * byte[], java.util.List)} or + * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, + * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request + * from only that peer. A RESPONDER may specify a null - indicating that + * it will accept connection requests from any device. + * @param token Deprecated and ignored. + * @return A string to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, + * android.net.ConnectivityManager.NetworkCallback)} + * [or other varieties of that API]. + */ + public String createNetworkSpecifier(@Nullable PeerHandle peerHandle, @Nullable byte[] token) { + return createNetworkSpecifierOpen(peerHandle); + } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 0eb6a3d157c3..3d784ba02fd6 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -130,55 +130,34 @@ public class WifiAwareManager { */ /** - * TYPE_1A: role, client_id, session_id, peer_id, token + * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk optional * @hide */ - public static final int NETWORK_SPECIFIER_TYPE_1A = 0; + public static final int NETWORK_SPECIFIER_TYPE_IB = 0; /** - * TYPE_1B: role, client_id, session_id, peer_id [only permitted for RESPONDER] + * TYPE: in band, any peer: role, client_id, session_id, pmk optional + * [only permitted for RESPONDER] * @hide */ - public static final int NETWORK_SPECIFIER_TYPE_1B = 1; + public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; /** - * TYPE_1C: role, client_id, session_id, token [only permitted for RESPONDER] + * TYPE: out-of-band: role, client_id, peer_mac, pmk optional * @hide */ - public static final int NETWORK_SPECIFIER_TYPE_1C = 2; + public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; /** - * TYPE_1C: role, client_id, session_id [only permitted for RESPONDER] + * TYPE: out-of-band, any peer: role, client_id, pmk optional + * [only permitted for RESPONDER] * @hide */ - public static final int NETWORK_SPECIFIER_TYPE_1D = 3; + public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; - /** - * TYPE_2A: role, client_id, peer_mac, token - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_2A = 4; - - /** - * TYPE_2B: role, client_id, peer_mac [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_2B = 5; - - /** - * TYPE_2C: role, client_id, token [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_2C = 6; - - /** - * TYPE_2D: role, client_id [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_2D = 7; /** @hide */ - public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_2D; + public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; /** @hide */ public static final String NETWORK_SPECIFIER_KEY_TYPE = "type"; @@ -199,7 +178,7 @@ public class WifiAwareManager { public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac"; /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token"; + public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk"; /** * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed. @@ -494,23 +473,15 @@ public class WifiAwareManager { /** @hide */ public String createNetworkSpecifier(int clientId, int role, int sessionId, - PeerHandle peerHandle, byte[] token) { + PeerHandle peerHandle, @Nullable byte[] pmk) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId) - + ", token=" + token); + + ", pmk=" + ((pmk == null) ? "null" : "non-null")); } - int type; - if (token != null && peerHandle != null) { - type = NETWORK_SPECIFIER_TYPE_1A; - } else if (token == null && peerHandle != null) { - type = NETWORK_SPECIFIER_TYPE_1B; - } else if (token != null && peerHandle == null) { - type = NETWORK_SPECIFIER_TYPE_1C; - } else { - type = NETWORK_SPECIFIER_TYPE_1D; - } + int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER + : NETWORK_SPECIFIER_TYPE_IB; if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { @@ -519,10 +490,6 @@ public class WifiAwareManager { + "specifier"); } if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) { - if (token == null) { - throw new IllegalArgumentException( - "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR"); - } if (peerHandle == null) { throw new IllegalArgumentException( "createNetworkSpecifier: Invalid peer handle (value of null) - not " @@ -540,10 +507,11 @@ public class WifiAwareManager { if (peerHandle != null) { json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId); } - if (token != null) { - json.put(NETWORK_SPECIFIER_KEY_TOKEN, - Base64.encodeToString(token, 0, token.length, Base64.DEFAULT)); + if (pmk == null) { + pmk = new byte[0]; } + json.put(NETWORK_SPECIFIER_KEY_PMK, + Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); } catch (JSONException e) { return ""; } @@ -553,21 +521,14 @@ public class WifiAwareManager { /** @hide */ public String createNetworkSpecifier(int clientId, @DataPathRole int role, - @Nullable byte[] peer, @Nullable byte[] token) { + @Nullable byte[] peer, @Nullable byte[] pmk) { if (VDBG) { - Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token); + Log.v(TAG, "createNetworkSpecifier: role=" + role + + ", pmk=" + ((pmk == null) ? "null" : "non-null")); } - int type; - if (token != null && peer != null) { - type = NETWORK_SPECIFIER_TYPE_2A; - } else if (token == null && peer != null) { - type = NETWORK_SPECIFIER_TYPE_2B; - } else if (token != null && peer == null) { - type = NETWORK_SPECIFIER_TYPE_2C; - } else { // both are null - type = NETWORK_SPECIFIER_TYPE_2D; - } + int type = (peer == null) ? + NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB; if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { @@ -576,20 +537,14 @@ public class WifiAwareManager { + "specifier"); } if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) { - if (peer == null || peer.length != 6) { - throw new IllegalArgumentException( - "createNetworkSpecifier: Invalid peer MAC address"); - } - if (token == null) { - throw new IllegalArgumentException( - "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR"); - } - } else { - if (peer != null && peer.length != 6) { - throw new IllegalArgumentException( - "createNetworkSpecifier: Invalid peer MAC address"); + if (peer == null) { + throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC " + + "address - null not permitted on INITIATOR"); } } + if (peer != null && peer.length != 6) { + throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); + } JSONObject json; try { @@ -600,10 +555,11 @@ public class WifiAwareManager { if (peer != null) { json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer))); } - if (token != null) { - json.put(NETWORK_SPECIFIER_KEY_TOKEN, - Base64.encodeToString(token, 0, token.length, Base64.DEFAULT)); + if (pmk == null) { + pmk = new byte[0]; } + json.put(NETWORK_SPECIFIER_KEY_PMK, + Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); } catch (JSONException e) { return ""; } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 86969200bc46..856066efff62 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -183,47 +183,114 @@ public class WifiAwareSession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a - * WiFi Aware connection to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an + * unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> * This API is targeted for applications which can obtain the peer MAC address using OOB * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - * when using Aware discovery use the alternative network specifier method - - * {@link DiscoverySession#createNetworkSpecifier(PeerHandle, - * byte[])}. + * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}. * * @param role The role of this device: * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this * value is used to gate the acceptance of a connection request from only that - * peer. A RESPONDER may specified a null - indicating that it will accept + * peer. A RESPONDER may specify a null - indicating that it will accept * connection requests from any device. - * @param token An arbitrary token (message) to be used to match connection initiation request - * to a responder setup. A RESPONDER is set up with a {@code token} which must - * be matched by the token provided by the INITIATOR. A null token is permitted - * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is - * not the same as a null token and requires the peer token to be empty as well. * * @return A string to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. + * + * @hide */ - public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer, @Nullable byte[] token) { + public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role, + @Nullable byte[] peer) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { - Log.e(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager"); + Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); return ""; } if (mTerminated) { - Log.e(TAG, "createNetworkSpecifier: called after termination"); + Log.e(TAG, "createNetworkSpecifierOpen: called after termination"); return ""; } - return mgr.createNetworkSpecifier(mClientId, role, peer, token); + return mgr.createNetworkSpecifier(mClientId, role, peer, null); + } + + /** + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an + * encrypted WiFi Aware connection (link) to the specified peer. The + * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to + * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. + * <p> + * This API is targeted for applications which can obtain the peer MAC address using OOB + * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - + * when using Aware discovery use the alternative network specifier method - + * {@link DiscoverySession#createNetworkSpecifierPmk(PeerHandle, byte[])}}. + * + * @param role The role of this device: + * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or + * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} + * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this + * value is used to gate the acceptance of a connection request from only that + * peer. A RESPONDER may specify a null - indicating that it will accept + * connection requests from any device. + * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for + * encrypting the data-path. Use the {@link #createNetworkSpecifierOpen(int, byte[])} + * to specify an open (unencrypted) link. + * + * @return A string to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, + * android.net.ConnectivityManager.NetworkCallback)} + * [or other varieties of that API]. + * + * @hide + */ + public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role, + @Nullable byte[] peer, @NonNull byte[] pmk) { + WifiAwareManager mgr = mMgr.get(); + if (mgr == null) { + Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); + return ""; + } + if (mTerminated) { + Log.e(TAG, "createNetworkSpecifierPmk: called after termination"); + return ""; + } + if (pmk == null || pmk.length == 0) { + throw new IllegalArgumentException("PMK must not be null or empty"); + } + return mgr.createNetworkSpecifier(mClientId, role, peer, pmk); + } + + /** + * Place-holder for {@code #createNetworkSpecifierOpen(int, byte[])}. Present to enable + * development of replacements CL without causing an API change. Will be removed when new + * APIs are exposed. + * + * @param role The role of this device: + * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or + * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} + * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this + * value is used to gate the acceptance of a connection request from only that + * peer. A RESPONDER may specify a null - indicating that it will accept + * connection requests from any device. + * @param token Deprecated and ignored. + * @return A string to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, + * android.net.ConnectivityManager.NetworkCallback)} + * [or other varieties of that API]. + */ + public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role, + @Nullable byte[] peer, @Nullable byte[] token) { + return createNetworkSpecifierOpen(role, peer); } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 7f68f6f128da..992958b84e1a 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -973,11 +973,11 @@ public class WifiAwareManagerTest { final int sessionId = 123; final PeerHandle peerHandle = new PeerHandle(123412); final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; - final String token = "Some arbitrary token string - can really be anything"; + final byte[] pmk = "Some arbitrary byte array".getBytes(); final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); - String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT); + String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); @@ -1008,9 +1008,8 @@ public class WifiAwareManagerTest { mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture()); - // (3) request a network specifier from the session - String networkSpecifier = publishSession.getValue().createNetworkSpecifier(peerHandle, - token.getBytes()); + // (3) request an open (unencrypted) network specifier from the session + String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle); // validate format JSONObject jsonObject = new JSONObject(networkSpecifier); @@ -1022,8 +1021,22 @@ public class WifiAwareManagerTest { equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); collector.checkThat("peer_id", peerHandle.peerId, equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); - collector.checkThat("token", tokenB64, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN))); + + // (4) request an encrypted (PMK) network specifier from the session + networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk); + + // validate format + jsonObject = new JSONObject(networkSpecifier); + collector.checkThat("role", role, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); + collector.checkThat("client_id", clientId, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); + collector.checkThat("session_id", sessionId, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); + collector.checkThat("peer_id", peerHandle.peerId, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); + collector.checkThat("pmk", pmkB64 , + equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); @@ -1039,9 +1052,9 @@ public class WifiAwareManagerTest { final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false); final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR; - final String token = "Some arbitrary token string - can really be anything"; + final byte[] pmk = "Some arbitrary pmk data".getBytes(); - String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT); + String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); @@ -1060,10 +1073,10 @@ public class WifiAwareManagerTest { inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); WifiAwareSession session = sessionCaptor.getValue(); - /* (2) request a direct network specifier*/ - String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes()); + // (2) request an open (unencrypted) direct network specifier + String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac); - /* validate format*/ + // validate format JSONObject jsonObject = new JSONObject(networkSpecifier); collector.checkThat("role", role, equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); @@ -1072,8 +1085,21 @@ public class WifiAwareManagerTest { collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), false))); - collector.checkThat("token", tokenB64, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN))); + + // (3) request an encrypted (PMK) direct network specifier + networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk); + + // validate format + jsonObject = new JSONObject(networkSpecifier); + collector.checkThat("role", role, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); + collector.checkThat("client_id", clientId, + equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); + collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( + jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), + false))); + collector.checkThat("pmk", pmkB64, + equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); |