Merge "Refactor KernelUidCpuTimeReader"
diff --git a/Android.mk b/Android.mk
index 58e21ff..59278b0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -143,7 +143,6 @@
bouncycastle \
okhttp \
ext \
- icu4j \
framework \
voip-common \
diff --git a/api/current.txt b/api/current.txt
index 573e635..fff502a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22842,6 +22842,27 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
+ public class MediaBrowser2 extends android.media.MediaController2 {
+ ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback);
+ method public void getChildren(java.lang.String, int, int, android.os.Bundle);
+ method public void getItem(java.lang.String);
+ method public void getLibraryRoot(android.os.Bundle);
+ method public void getSearchResult(java.lang.String, int, int, android.os.Bundle);
+ method public void search(java.lang.String, android.os.Bundle);
+ method public void subscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String);
+ }
+
+ public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback {
+ ctor public MediaBrowser2.BrowserCallback();
+ method public void onChildrenChanged(java.lang.String, int, android.os.Bundle);
+ method public void onGetChildrenDone(java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
+ method public void onGetItemDone(java.lang.String, android.media.MediaItem2);
+ method public void onGetLibraryRootDone(android.os.Bundle, java.lang.String, android.os.Bundle);
+ method public void onGetSearchResultDone(java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
+ method public void onSearchResultChanged(java.lang.String, int, android.os.Bundle);
+ }
+
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
@@ -23318,6 +23339,73 @@
field public static final int REGULAR_CODECS = 0; // 0x0
}
+ public class MediaController2 implements java.lang.AutoCloseable {
+ ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
+ method public void addPlaylistItem(int, android.media.MediaItem2);
+ method public void adjustVolume(int, int);
+ method public void close();
+ method public void fastForward();
+ method public long getBufferedPosition();
+ method public android.media.MediaItem2 getCurrentPlaylistItem();
+ method public android.media.MediaController2.PlaybackInfo getPlaybackInfo();
+ method public float getPlaybackSpeed();
+ method public int getPlayerState();
+ method public java.util.List<android.media.MediaItem2> getPlaylist();
+ method public android.media.MediaSession2.PlaylistParams getPlaylistParams();
+ method public long getPosition();
+ method public android.app.PendingIntent getSessionActivity();
+ method public android.media.SessionToken2 getSessionToken();
+ method public boolean isConnected();
+ method public void pause();
+ method public void play();
+ method public void playFromMediaId(java.lang.String, android.os.Bundle);
+ method public void playFromSearch(java.lang.String, android.os.Bundle);
+ method public void playFromUri(android.net.Uri, android.os.Bundle);
+ method public void prepare();
+ method public void prepareFromMediaId(java.lang.String, android.os.Bundle);
+ method public void prepareFromSearch(java.lang.String, android.os.Bundle);
+ method public void prepareFromUri(android.net.Uri, android.os.Bundle);
+ method public void removePlaylistItem(android.media.MediaItem2);
+ method public void rewind();
+ method public void seekTo(long);
+ method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+ method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
+ method public void setRating(java.lang.String, android.media.Rating2);
+ method public void setVolumeTo(int, int);
+ method public void skipToNext();
+ method public void skipToPlaylistItem(android.media.MediaItem2);
+ method public void skipToPrevious();
+ method public void stop();
+ }
+
+ public static abstract class MediaController2.ControllerCallback {
+ ctor public MediaController2.ControllerCallback();
+ method public void onAllowedCommandsChanged(android.media.MediaSession2.CommandGroup);
+ method public void onBufferedPositionChanged(long);
+ method public void onConnected(android.media.MediaSession2.CommandGroup);
+ method public void onCurrentPlaylistItemChanged(android.media.MediaItem2);
+ method public void onCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+ method public void onCustomLayoutChanged(java.util.List<android.media.MediaSession2.CommandButton>);
+ method public void onDisconnected();
+ method public void onError(int, int);
+ method public void onPlaybackInfoChanged(android.media.MediaController2.PlaybackInfo);
+ method public void onPlaybackSpeedChanged(float);
+ method public void onPlayerStateChanged(int);
+ method public void onPlaylistChanged(java.util.List<android.media.MediaItem2>);
+ method public void onPlaylistParamsChanged(android.media.MediaSession2.PlaylistParams);
+ method public void onPositionUpdated(long, long);
+ }
+
+ public static final class MediaController2.PlaybackInfo {
+ method public android.media.AudioAttributes getAudioAttributes();
+ method public int getControlType();
+ method public int getCurrentVolume();
+ method public int getMaxVolume();
+ method public int getPlaybackType();
+ field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+ field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+ }
+
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
@@ -23713,6 +23801,69 @@
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
+ public class MediaItem2 {
+ method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle);
+ method public android.media.DataSourceDesc getDataSourceDesc();
+ method public int getFlags();
+ method public java.lang.String getMediaId();
+ method public android.media.MediaMetadata2 getMetadata();
+ method public boolean isBrowsable();
+ method public boolean isPlayable();
+ method public void setMetadata(android.media.MediaMetadata2);
+ method public android.os.Bundle toBundle();
+ field public static final int FLAG_BROWSABLE = 1; // 0x1
+ field public static final int FLAG_PLAYABLE = 2; // 0x2
+ }
+
+ public static final class MediaItem2.Builder {
+ ctor public MediaItem2.Builder(android.content.Context, int);
+ method public android.media.MediaItem2 build();
+ method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc);
+ method public android.media.MediaItem2.Builder setMediaId(java.lang.String);
+ method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2);
+ }
+
+ public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 {
+ ctor public MediaLibraryService2();
+ method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
+ }
+
+ public static final class MediaLibraryService2.LibraryRoot {
+ ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle);
+ method public android.os.Bundle getExtras();
+ method public java.lang.String getRootId();
+ field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
+ field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT";
+ field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
+ }
+
+ public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 {
+ method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
+ method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle);
+ method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
+ }
+
+ public static final class MediaLibraryService2.MediaLibrarySession.Builder {
+ ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, android.media.MediaPlayerBase, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
+ method public android.media.MediaLibraryService2.MediaLibrarySession build();
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String);
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
+ }
+
+ public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback {
+ ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context);
+ method public java.util.List<android.media.MediaItem2> onGetChildren(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
+ method public android.media.MediaItem2 onGetItem(android.media.MediaSession2.ControllerInfo, java.lang.String);
+ method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaSession2.ControllerInfo, android.os.Bundle);
+ method public java.util.List<android.media.MediaItem2> onGetSearchResult(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
+ method public void onSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onSubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onUnsubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String);
+ }
+
public final class MediaMetadata implements android.os.Parcelable {
method public boolean containsKey(java.lang.String);
method public int describeContents();
@@ -23768,6 +23919,79 @@
method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
}
+ public final class MediaMetadata2 {
+ method public boolean containsKey(java.lang.String);
+ method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle);
+ method public android.graphics.Bitmap getBitmap(java.lang.String);
+ method public android.os.Bundle getExtras();
+ method public float getFloat(java.lang.String);
+ method public long getLong(java.lang.String);
+ method public java.lang.String getMediaId();
+ method public android.media.Rating2 getRating(java.lang.String);
+ method public java.lang.String getString(java.lang.String);
+ method public java.lang.CharSequence getText(java.lang.String);
+ method public java.util.Set<java.lang.String> keySet();
+ method public int size();
+ method public android.os.Bundle toBundle();
+ field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+ field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+ field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+ field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+ field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+ field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+ field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+ field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+ field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+ field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+ field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+ field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+ field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+ field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+ field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+ field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+ field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+ field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+ field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+ field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+ field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+ field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+ field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+ field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+ field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+ field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
+ field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+ field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+ field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+ field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+ field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN";
+ field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY";
+ field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+ field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+ field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+ field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+ field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+ field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+ field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+ field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+ field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+ }
+
+ public static final class MediaMetadata2.Builder {
+ ctor public MediaMetadata2.Builder(android.content.Context);
+ ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2);
+ method public android.media.MediaMetadata2 build();
+ method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+ method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float);
+ method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long);
+ method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2);
+ method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String);
+ method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence);
+ method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle);
+ }
+
public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
@@ -24239,6 +24463,19 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
+ public abstract class MediaPlayerBase implements java.lang.AutoCloseable {
+ ctor public MediaPlayerBase();
+ method public abstract android.media.AudioAttributes getAudioAttributes();
+ method public abstract int getPlayerState();
+ method public abstract void pause();
+ method public abstract void play();
+ method public abstract void setAudioAttributes(android.media.AudioAttributes);
+ field public static final int STATE_ERROR = 0; // 0x0
+ field public static final int STATE_IDLE = 0; // 0x0
+ field public static final int STATE_PAUSED = 0; // 0x0
+ field public static final int STATE_PLAYING = 0; // 0x0
+ }
+
public class MediaRecorder implements android.media.AudioRouting {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -24515,6 +24752,172 @@
method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
}
+ public class MediaSession2 implements java.lang.AutoCloseable {
+ method public void addPlaylistItem(int, android.media.MediaItem2);
+ method public void close();
+ method public void editPlaylistItem(android.media.MediaItem2);
+ method public void fastForward();
+ method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
+ method public android.media.MediaItem2 getCurrentPlaylistItem();
+ method public android.media.MediaPlayerBase getPlayer();
+ method public java.util.List<android.media.MediaItem2> getPlaylist();
+ method public android.media.MediaSession2.PlaylistParams getPlaylistParams();
+ method public android.media.SessionToken2 getToken();
+ method public void notifyError(int, int);
+ method public void pause();
+ method public void play();
+ method public void prepare();
+ method public void removePlaylistItem(android.media.MediaItem2);
+ method public void rewind();
+ method public void seekTo(long);
+ method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
+ method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+ method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
+ method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
+ method public void setPlayer(android.media.MediaPlayerBase);
+ method public void setPlayer(android.media.MediaPlayerBase, android.media.VolumeProvider2);
+ method public void setPlaylist(java.util.List<android.media.MediaItem2>);
+ method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
+ method public void skipToNext();
+ method public void skipToPlaylistItem(android.media.MediaItem2);
+ method public void skipToPrevious();
+ method public void stop();
+ field public static final int COMMAND_CODE_BROWSER = 22; // 0x16
+ field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+ field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
+ field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2
+ field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1
+ field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
+ field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
+ field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
+ field public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10; // 0xa
+ field public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 11; // 0xb
+ field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
+ field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
+ field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
+ field public static final int COMMAND_CODE_PLAYLIST_ADD = 12; // 0xc
+ field public static final int COMMAND_CODE_PLAYLIST_GET = 14; // 0xe
+ field public static final int COMMAND_CODE_PLAYLIST_REMOVE = 13; // 0xd
+ field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16; // 0x10
+ field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 18; // 0x12
+ field public static final int COMMAND_CODE_PLAY_FROM_URI = 17; // 0x11
+ field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 19; // 0x13
+ field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 21; // 0x15
+ field public static final int COMMAND_CODE_PREPARE_FROM_URI = 20; // 0x14
+ field public static final int COMMAND_CODE_SET_VOLUME = 15; // 0xf
+ field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+ field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+ field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+ field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+ field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+ field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+ field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+ field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+ field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+ field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc
+ field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+ field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+ }
+
+ public static final class MediaSession2.Builder {
+ ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase);
+ method public android.media.MediaSession2 build();
+ method public android.media.MediaSession2.Builder setId(java.lang.String);
+ method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
+ method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
+ method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
+ }
+
+ public static final class MediaSession2.Command {
+ ctor public MediaSession2.Command(android.content.Context, int);
+ ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
+ method public int getCommandCode();
+ method public java.lang.String getCustomCommand();
+ method public android.os.Bundle getExtra();
+ }
+
+ public static final class MediaSession2.CommandButton {
+ method public android.media.MediaSession2.Command getCommand();
+ method public java.lang.String getDisplayName();
+ method public android.os.Bundle getExtra();
+ method public int getIconResId();
+ method public boolean isEnabled();
+ }
+
+ public static final class MediaSession2.CommandButton.Builder {
+ ctor public MediaSession2.CommandButton.Builder(android.content.Context);
+ method public android.media.MediaSession2.CommandButton build();
+ method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
+ method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
+ method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
+ method public android.media.MediaSession2.CommandButton.Builder setExtra(android.os.Bundle);
+ method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
+ }
+
+ public static final class MediaSession2.CommandGroup {
+ ctor public MediaSession2.CommandGroup(android.content.Context);
+ ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
+ method public void addAllPredefinedCommands();
+ method public void addCommand(android.media.MediaSession2.Command);
+ method public boolean hasCommand(android.media.MediaSession2.Command);
+ method public boolean hasCommand(int);
+ method public void removeCommand(android.media.MediaSession2.Command);
+ }
+
+ public static final class MediaSession2.ControllerInfo {
+ method public java.lang.String getPackageName();
+ method public int getUid();
+ method public boolean isTrusted();
+ }
+
+ public static final class MediaSession2.PlaylistParams {
+ ctor public MediaSession2.PlaylistParams(android.content.Context, int, int, android.media.MediaMetadata2);
+ method public static android.media.MediaSession2.PlaylistParams fromBundle(android.content.Context, android.os.Bundle);
+ method public android.media.MediaMetadata2 getPlaylistMetadata();
+ method public int getRepeatMode();
+ method public int getShuffleMode();
+ method public android.os.Bundle toBundle();
+ field public static final int REPEAT_MODE_ALL = 2; // 0x2
+ field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+ field public static final int REPEAT_MODE_NONE = 0; // 0x0
+ field public static final int REPEAT_MODE_ONE = 1; // 0x1
+ field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+ field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+ field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+ }
+
+ public static abstract class MediaSession2.SessionCallback {
+ ctor public MediaSession2.SessionCallback(android.content.Context);
+ method public boolean onCommandRequest(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
+ method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2.ControllerInfo);
+ method public void onCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+ method public void onDisconnected(android.media.MediaSession2.ControllerInfo);
+ method public void onPlayFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onPlayFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onPlayFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
+ method public void onPrepareFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onPrepareFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+ method public void onPrepareFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
+ method public void onSetRating(android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
+ }
+
+ public abstract class MediaSessionService2 extends android.app.Service {
+ ctor public MediaSessionService2();
+ method public final android.media.MediaSession2 getSession();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.media.MediaSession2 onCreateSession(java.lang.String);
+ method public android.media.MediaSessionService2.MediaNotification onUpdateNotification();
+ field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2";
+ field public static final java.lang.String SERVICE_META_DATA = "android.media.session";
+ }
+
+ public static class MediaSessionService2.MediaNotification {
+ ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification);
+ method public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
public final class MediaSync {
ctor public MediaSync();
method public android.view.Surface createInputSurface();
@@ -24645,6 +25048,29 @@
field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
}
+ public final class Rating2 {
+ method public static android.media.Rating2 fromBundle(android.content.Context, android.os.Bundle);
+ method public float getPercentRating();
+ method public int getRatingStyle();
+ method public float getStarRating();
+ method public boolean hasHeart();
+ method public boolean isRated();
+ method public boolean isThumbUp();
+ method public static android.media.Rating2 newHeartRating(android.content.Context, boolean);
+ method public static android.media.Rating2 newPercentageRating(android.content.Context, float);
+ method public static android.media.Rating2 newStarRating(android.content.Context, int, float);
+ method public static android.media.Rating2 newThumbRating(android.content.Context, boolean);
+ method public static android.media.Rating2 newUnratedRating(android.content.Context, int);
+ method public android.os.Bundle toBundle();
+ field public static final int RATING_3_STARS = 3; // 0x3
+ field public static final int RATING_4_STARS = 4; // 0x4
+ field public static final int RATING_5_STARS = 5; // 0x5
+ field public static final int RATING_HEART = 1; // 0x1
+ field public static final int RATING_NONE = 0; // 0x0
+ field public static final int RATING_PERCENTAGE = 6; // 0x6
+ field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+ }
+
public deprecated class RemoteControlClient {
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -24780,6 +25206,22 @@
field public static final int URI_COLUMN_INDEX = 2; // 0x2
}
+ public final class SessionToken2 {
+ ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String);
+ method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle);
+ method public java.lang.String getId();
+ method public java.lang.String getPackageName();
+ method public int getType();
+ method public int getUid();
+ method public android.os.Bundle toBundle();
+ field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
+ field public static final int TYPE_SESSION = 0; // 0x0
+ field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+ }
+
+ public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation {
+ }
+
public class SoundPool {
ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
@@ -24983,6 +25425,19 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
+ public abstract class VolumeProvider2 {
+ ctor public VolumeProvider2(android.content.Context, int, int, int);
+ method public final int getControlType();
+ method public final int getCurrentVolume();
+ method public final int getMaxVolume();
+ method public void onAdjustVolume(int);
+ method public void onSetVolumeTo(int);
+ method public final void setCurrentVolume(int);
+ field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+ field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+ field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+ }
+
public final class VolumeShaper implements java.lang.AutoCloseable {
method public void apply(android.media.VolumeShaper.Operation);
method public void close();
@@ -25729,14 +26184,23 @@
public final class MediaSessionManager {
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
+ method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
+ method public java.util.List<android.media.SessionToken2> getActiveSessionTokens();
method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+ method public java.util.List<android.media.SessionToken2> getAllSessionTokens();
+ method public java.util.List<android.media.SessionToken2> getSessionServiceTokens();
method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+ method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
}
public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
}
+ public static abstract interface MediaSessionManager.OnSessionTokensChangedListener {
+ method public abstract void onSessionTokensChanged(java.util.List<android.media.SessionToken2>);
+ }
+
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
diff --git a/api/system-current.txt b/api/system-current.txt
index a581bab..3aca59a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6130,7 +6130,9 @@
method public final void onSmsReceived(int, java.lang.String, byte[]) throws java.lang.RuntimeException;
method public final void onSmsStatusReportReceived(int, int, java.lang.String, byte[]) throws java.lang.RuntimeException;
method public void sendSms(int, int, java.lang.String, java.lang.String, boolean, byte[]);
- field public static final int DELIVER_STATUS_ERROR = 2; // 0x2
+ field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
+ field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
+ field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
field public static final int DELIVER_STATUS_OK = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 87825f1..d7ce352 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -36,6 +36,7 @@
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
src/external/ResourceHealthManagerPuller.cpp \
+ src/external/ResourceThermalManagerPuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
src/external/KernelUidCpuActiveTimeReader.cpp \
@@ -99,6 +100,7 @@
android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
+ android.hardware.thermal@1.0 \
libmemunreachable
# =========
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 04ebfcd..7159b9b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -72,7 +72,7 @@
BatteryLevelChanged battery_level_changed = 30;
ChargingStateChanged charging_state_changed = 31;
PluggedStateChanged plugged_state_changed = 32;
- DeviceTemperatureReported device_temperature_reported = 33;
+ // TODO: 33 is blank, but is available for use.
DeviceOnStatusChanged device_on_status_changed = 34;
WakeupAlarmOccurred wakeup_alarm_occurred = 35;
KernelWakeupReported kernel_wakeup_reported = 36;
@@ -105,7 +105,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10021
+ // Next: 10022
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -128,6 +128,7 @@
DiskSpace disk_space = 10018;
RemainingBatteryCapacity remaining_battery_capacity = 10019;
FullBatteryCapacity full_battery_capacity = 10020;
+ Temperature temperature = 10021;
}
}
@@ -536,17 +537,6 @@
optional android.os.BatteryPluggedStateEnum state = 1;
}
-/**
- * Logs the temperature of the device, in tenths of a degree Celsius.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message DeviceTemperatureReported {
- // Temperature in tenths of a degree C.
- optional int32 temperature = 1;
-}
-
// TODO: Define this more precisely.
// TODO: Log the ON state somewhere. It isn't currently logged anywhere.
/**
@@ -1508,7 +1498,8 @@
/**
* Pulls battery coulomb counter, which is the remaining battery charge in uAh.
- * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
*/
message RemainingBatteryCapacity {
optional int32 charge_uAh = 1;
@@ -1516,8 +1507,26 @@
/**
* Pulls battery capacity, which is the battery capacity when full in uAh.
- * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
*/
message FullBatteryCapacity {
optional int32 capacity_uAh = 1;
+}
+
+/**
+ * Pulls the temperature of various parts of the device, in Celsius.
+ *
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+ */
+message Temperature {
+ // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY.
+ optional android.os.TemperatureTypeEnum sensor_location = 1;
+
+ // The name of the temperature source. Eg. CPU0
+ optional string sensor_name = 2;
+
+ // Temperature in degrees C.
+ optional float temperature_C = 3;
}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index 261cb43..3741202 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <android/hardware/health/2.0/IHealth.h>
@@ -47,7 +47,6 @@
bool getHealthHal() {
if (gHealthHal == nullptr) {
gHealthHal = get_health_service();
-
}
return gHealthHal != nullptr;
}
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
new file mode 100644
index 0000000..b3acdfc
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include <android/hardware/thermal/1.0/IThermal.h>
+#include "external/ResourceThermalManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceThermalManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+#include "stats_log_util.h"
+
+#include <chrono>
+
+using android::hardware::hidl_death_recipient;
+using android::hardware::hidl_vec;
+using android::hidl::base::V1_0::IBase;
+using android::hardware::thermal::V1_0::IThermal;
+using android::hardware::thermal::V1_0::Temperature;
+using android::hardware::thermal::V1_0::ThermalStatus;
+using android::hardware::thermal::V1_0::ThermalStatusCode;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::chrono::duration_cast;
+using std::chrono::nanoseconds;
+using std::chrono::system_clock;
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool getThermalHalLocked();
+sp<android::hardware::thermal::V1_0::IThermal> gThermalHal = nullptr;
+std::mutex gThermalHalMutex;
+
+struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
+ std::lock_guard<std::mutex> lock(gThermalHalMutex);
+ ALOGE("ThermalHAL just died");
+ gThermalHal = nullptr;
+ getThermalHalLocked();
+ }
+};
+
+sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr;
+
+// The caller must be holding gThermalHalMutex.
+bool getThermalHalLocked() {
+ if (gThermalHal == nullptr) {
+ gThermalHal = IThermal::getService();
+ if (gThermalHal == nullptr) {
+ ALOGE("Unable to get Thermal service.");
+ } else {
+ if (gThermalHalDeathRecipient == nullptr) {
+ gThermalHalDeathRecipient = new ThermalHalDeathRecipient();
+ }
+ hardware::Return<bool> linked = gThermalHal->linkToDeath(
+ gThermalHalDeathRecipient, 0x451F /* cookie */);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to ThermalHAL death: %s",
+ linked.description().c_str());
+ gThermalHal = nullptr;
+ } else if (!linked) {
+ ALOGW("Unable to link to ThermalHal death notifications");
+ gThermalHal = nullptr;
+ } else {
+ ALOGD("Link to death notification successful");
+ }
+ }
+ }
+ return gThermalHal != nullptr;
+}
+
+ResourceThermalManagerPuller::ResourceThermalManagerPuller() :
+ StatsPuller(android::util::TEMPERATURE) {
+}
+
+bool ResourceThermalManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ std::lock_guard<std::mutex> lock(gThermalHalMutex);
+ if (!getThermalHalLocked()) {
+ ALOGE("Thermal Hal not loaded");
+ return false;
+ }
+
+ int64_t wallClockTimestampNs = getWallClockNs();
+ int64_t elapsedTimestampNs = getElapsedRealtimeNs();
+
+ data->clear();
+ bool resultSuccess = true;
+
+ Return<void> ret = gThermalHal->getTemperatures(
+ [&](ThermalStatus status, const hidl_vec<Temperature>& temps) {
+ if (status.code != ThermalStatusCode::SUCCESS) {
+ ALOGE("Failed to get temperatures from ThermalHAL. Status: %d", status.code);
+ resultSuccess = false;
+ return;
+ }
+ if (mTagId == android::util::TEMPERATURE) {
+ for (size_t i = 0; i < temps.size(); ++i) {
+ auto ptr = make_shared<LogEvent>(android::util::TEMPERATURE,
+ wallClockTimestampNs, elapsedTimestampNs);
+ ptr->write((static_cast<int>(temps[i].type)));
+ ptr->write(temps[i].name);
+ ptr->write(temps[i].currentValue);
+ ptr->init();
+ data->push_back(ptr);
+ }
+ } else {
+ ALOGE("Unsupported tag in ResourceThermalManagerPuller: %d", mTagId);
+ }
+ });
+ if (!ret.isOk()) {
+ ALOGE("getThermalHalLocked() failed: thermal HAL service not available. Description: %s",
+ ret.description().c_str());
+ if (ret.isDeadObject()) {
+ gThermalHal = nullptr;
+ }
+ return false;
+ }
+ return resultSuccess;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.h b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
new file mode 100644
index 0000000..13c675a
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads IThermal.hal
+ */
+class ResourceThermalManagerPuller : public StatsPuller {
+public:
+ ResourceThermalManagerPuller();
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index bee9939..880dfd1 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,10 +23,10 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
-#include "ResourceHealthManagerPuller.h"
#include "KernelUidCpuActiveTimeReader.h"
#include "KernelUidCpuClusterTimeReader.h"
-#include "SubsystemSleepStatePuller.h"
+#include "ResourceHealthManagerPuller.h"
+#include "ResourceThermalManagerPuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
@@ -118,7 +118,9 @@
{{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4,5,6,7,8}, {2,3}, 0, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}};
+ {{4,5,6,7,8}, {2,3}, 0, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+ // temperature
+ {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX) {
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index eae7d70..3d072c5 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -63,6 +63,8 @@
/**
* Device temperature types.
*/
+ // These constants are also defined in android/os/enums.proto.
+ // Any change to the types here or in the thermal hal should be made in the proto as well.
/** Temperature of CPUs in Celsius. */
public static final int DEVICE_TEMPERATURE_CPU = Constants.TemperatureType.CPU;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0442c9c..cb38c0f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7103,6 +7103,14 @@
SHOW_ROTATION_SUGGESTIONS_ENABLED;
/**
+ * The number of accepted rotation suggestions. Used to determine if the user has been
+ * introduced to rotation suggestions.
+ * @hide
+ */
+ public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED =
+ "num_rotation_suggestions_accepted";
+
+ /**
* Read only list of the service components that the current user has explicitly allowed to
* see and assist with all of the user's notifications.
*
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 6da51d1..db19681 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -1008,10 +1008,8 @@
final long vsync = AnimationUtils.currentAnimationTimeMillis() * 1000000L;
mFrameInfo.setVsync(vsync, vsync);
mFrameInfo.addFlags(1 << 2 /* VSYNC */);
- // TODO: remove this fence
- nFence(mNativeProxy);
if (callback != null) {
- callback.onFrameDraw(mSurface.getNextFrameNumber());
+ nSetFrameCallback(mNativeProxy, callback);
}
nSyncAndDrawFrame(mNativeProxy, mFrameInfo.mFrameInfo, mFrameInfo.mFrameInfo.length);
}
@@ -1184,6 +1182,7 @@
private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
private static native void nSetContentDrawBounds(long nativeProxy, int left,
int top, int right, int bottom);
+ private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index dc50fa1..d6c43e8 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -105,11 +105,13 @@
boolean hit = true;
boolean handled = false;
- switch (event.getAction()) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDelegateTargeted = mBounds.contains(x, y);
sendToDelegate = mDelegateTargeted;
break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
sendToDelegate = mDelegateTargeted;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5b1dd5c..af6c701 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3333,7 +3333,7 @@
final int actionCount = mActions.size();
int nonStandardActionCount = 0;
- int defaultStandardActions = 0;
+ long defaultStandardActions = 0;
for (int i = 0; i < actionCount; i++) {
AccessibilityAction action = mActions.get(i);
if (isDefaultStandardAction(action)) {
@@ -3342,7 +3342,7 @@
nonStandardActionCount++;
}
}
- parcel.writeInt(defaultStandardActions);
+ parcel.writeLong(defaultStandardActions);
parcel.writeInt(nonStandardActionCount);
for (int i = 0; i < actionCount; i++) {
@@ -3540,7 +3540,7 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
- final int standardActions = parcel.readInt();
+ final long standardActions = parcel.readLong();
addStandardActions(standardActions);
final int nonStandardActionCount = parcel.readInt();
for (int i = 0; i < nonStandardActionCount; i++) {
@@ -3636,7 +3636,7 @@
return null;
}
- private static AccessibilityAction getActionSingletonBySerializationFlag(int flag) {
+ private static AccessibilityAction getActionSingletonBySerializationFlag(long flag) {
final int actions = AccessibilityAction.sStandardActions.size();
for (int i = 0; i < actions; i++) {
AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
@@ -3648,10 +3648,10 @@
return null;
}
- private void addStandardActions(int serializationIdMask) {
- int remainingIds = serializationIdMask;
+ private void addStandardActions(long serializationIdMask) {
+ long remainingIds = serializationIdMask;
while (remainingIds > 0) {
- final int id = 1 << Integer.numberOfTrailingZeros(remainingIds);
+ final int id = 1 << Long.numberOfTrailingZeros(remainingIds);
remainingIds &= ~id;
AccessibilityAction action = getActionSingletonBySerializationFlag(id);
addAction(action);
@@ -4276,7 +4276,7 @@
private final CharSequence mLabel;
/** @hide */
- public int mSerializationFlag = -1;
+ public long mSerializationFlag = -1L;
/**
* Creates a new AccessibilityAction. For adding a standard action without a specific label,
@@ -4310,7 +4310,7 @@
private AccessibilityAction(int standardActionId) {
this(standardActionId, null);
- mSerializationFlag = (int) bitAt(sStandardActions.size());
+ mSerializationFlag = bitAt(sStandardActions.size());
sStandardActions.add(this);
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index eb2af60..6e87e23 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -483,12 +483,12 @@
* Destroys this instance.
*/
public void destroy() {
- mRenderer.destroy();
- mSurface.destroy();
- mSurfaceControl.destroy();
- mSurfaceSession.kill();
- mBitmapRenderNode.destroy();
synchronized (mLock) {
+ mRenderer.destroy();
+ mSurface.destroy();
+ mSurfaceControl.destroy();
+ mSurfaceSession.kill();
+ mBitmapRenderNode.destroy();
mHandler.removeCallbacks(mMagnifierUpdater);
if (mBitmap != null) {
mBitmap.recycle();
@@ -530,17 +530,22 @@
final int pendingY = mWindowPositionY;
callback = frame -> {
- mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
- // Show or move the window at the content draw frame.
- SurfaceControl.openTransaction();
- mSurfaceControl.deferTransactionUntil(mSurface, frame);
- if (updateWindowPosition) {
- mSurfaceControl.setPosition(pendingX, pendingY);
+ synchronized (mLock) {
+ if (!mSurface.isValid()) {
+ return;
+ }
+ mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
+ // Show or move the window at the content draw frame.
+ SurfaceControl.openTransaction();
+ mSurfaceControl.deferTransactionUntil(mSurface, frame);
+ if (updateWindowPosition) {
+ mSurfaceControl.setPosition(pendingX, pendingY);
+ }
+ if (firstDraw) {
+ mSurfaceControl.show();
+ }
+ SurfaceControl.closeTransaction();
}
- if (firstDraw) {
- mSurfaceControl.show();
- }
- SurfaceControl.closeTransaction();
};
} else {
callback = null;
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 7d556bf..273b9ed 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -80,46 +80,57 @@
/**
* MediaControlView2 button value for playing and pausing media.
+ * @hide
*/
public static final int BUTTON_PLAY_PAUSE = 1;
/**
* MediaControlView2 button value for jumping 30 seconds forward.
+ * @hide
*/
public static final int BUTTON_FFWD = 2;
/**
* MediaControlView2 button value for jumping 10 seconds backward.
+ * @hide
*/
public static final int BUTTON_REW = 3;
/**
* MediaControlView2 button value for jumping to next media.
+ * @hide
*/
public static final int BUTTON_NEXT = 4;
/**
* MediaControlView2 button value for jumping to previous media.
+ * @hide
*/
public static final int BUTTON_PREV = 5;
/**
* MediaControlView2 button value for showing/hiding subtitle track.
+ * @hide
*/
public static final int BUTTON_SUBTITLE = 6;
/**
* MediaControlView2 button value for toggling full screen.
+ * @hide
*/
public static final int BUTTON_FULL_SCREEN = 7;
/**
* MediaControlView2 button value for showing/hiding overflow buttons.
+ * @hide
*/
public static final int BUTTON_OVERFLOW = 8;
/**
* MediaControlView2 button value for muting audio.
+ * @hide
*/
public static final int BUTTON_MUTE = 9;
/**
* MediaControlView2 button value for adjusting aspect ratio of view.
+ * @hide
*/
public static final int BUTTON_ASPECT_RATIO = 10;
/**
* MediaControlView2 button value for showing/hiding settings page.
+ * @hide
*/
public static final int BUTTON_SETTINGS = 11;
@@ -187,6 +198,7 @@
* <li>{@link #BUTTON_SETTINGS}
* </ul>
* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+ * @hide
*/
public void setButtonVisibility(@Button int button, @Visibility int visibility) {
mProvider.setButtonVisibility_impl(button, visibility);
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 340be46..a7ae3234 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -23,7 +23,7 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
@@ -277,7 +277,7 @@
* @hide
*/
public void setRouteAttributes(@NonNull List<String> routeCategories,
- @Nullable MediaPlayerInterface player) {
+ @Nullable MediaPlayerBase player) {
mProvider.setRouteAttributes_impl(routeCategories, player);
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 293471c..ffc21d5 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -319,7 +319,7 @@
}
final PackageManager pm = mContext.getPackageManager();
String label = serviceInfo.getResolveInfo().loadLabel(pm).toString();
- String summary = serviceInfo.loadSummary(pm).toString();
+ CharSequence summary = serviceInfo.loadSummary(pm);
if (!includeSummary || TextUtils.isEmpty(summary)) {
return label;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1a6cdfe..8ee31f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12606,7 +12606,7 @@
temp = Math.max(0, temp);
reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
- status, plugType, level, temp);
+ status, plugType, level);
final boolean onBattery = isOnBattery(plugType, status);
final long uptime = mClocks.uptimeMillis();
@@ -12805,7 +12805,7 @@
// Inform StatsLog of setBatteryState changes.
// If this is the first reporting, pass in recentPast == null.
private void reportChangesToStatsLog(HistoryItem recentPast,
- final int status, final int plugType, final int level, final int temp) {
+ final int status, final int plugType, final int level) {
if (recentPast == null || recentPast.batteryStatus != status) {
StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, status);
@@ -12816,8 +12816,6 @@
if (recentPast == null || recentPast.batteryLevel != level) {
StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, level);
}
- // Let's just always print the temperature, regardless of whether it changed.
- StatsLog.write(StatsLog.DEVICE_TEMPERATURE_REPORTED, temp);
}
public long getAwakeTimeBattery() {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 9f3475a..13e0e4a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -68,6 +68,10 @@
jmethodID callback;
} gFrameMetricsObserverClassInfo;
+struct {
+ jmethodID onFrameDraw;
+} gFrameDrawingCallback;
+
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -849,6 +853,44 @@
proxy->setContentDrawBounds(left, top, right, bottom);
}
+class JGlobalRefHolder {
+public:
+ JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
+
+ virtual ~JGlobalRefHolder() {
+ getenv(mVm)->DeleteGlobalRef(mObject);
+ mObject = nullptr;
+ }
+
+ jobject object() { return mObject; }
+ JavaVM* vm() { return mVm; }
+
+private:
+ JGlobalRefHolder(const JGlobalRefHolder&) = delete;
+ void operator=(const JGlobalRefHolder&) = delete;
+
+ JavaVM* mVm;
+ jobject mObject;
+};
+
+static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
+ jobject clazz, jlong proxyPtr, jobject frameCallback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!frameCallback) {
+ proxy->setFrameCallback(nullptr);
+ } else {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
+ env->NewGlobalRef(frameCallback));
+ proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
+ static_cast<jlong>(frameNr));
+ });
+ }
+}
+
static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
jobject clazz, jobject jsurface, jint left, jint top,
jint right, jint bottom, jobject jbitmap) {
@@ -1034,6 +1076,8 @@
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
{ "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
+ { "nSetFrameCallback", "(JLandroid/view/ThreadedRenderer$FrameDrawingCallback;)V",
+ (void*)android_view_ThreadedRenderer_setFrameCallback},
{ "nAddFrameMetricsObserver",
"(JLandroid/view/FrameMetricsObserver;)J",
(void*)android_view_ThreadedRenderer_addFrameMetricsObserver },
@@ -1078,6 +1122,11 @@
gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
env, metricsClass, "mTimingData", "[J");
+ jclass frameCallbackClass = FindClassOrDie(env,
+ "android/view/ThreadedRenderer$FrameDrawingCallback");
+ gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
+ "onFrameDraw", "(J)V");
+
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto
index fe9b7ac..aa99ac7 100644
--- a/core/proto/android/os/enums.proto
+++ b/core/proto/android/os/enums.proto
@@ -56,6 +56,17 @@
BATTERY_STATUS_FULL = 5;
}
+// These constants are defined in hardware/interfaces/thermal/1.0/types.hal
+// They are primarily used by android/os/HardwarePropertiesManager.java.
+// Any change to the types in the thermal hal should be made here as well.
+enum TemperatureTypeEnum {
+ TEMPERATURE_TYPE_UKNOWN = -1;
+ TEMPERATURE_TYPE_CPU = 0;
+ TEMPERATURE_TYPE_GPU = 1;
+ TEMPERATURE_TYPE_BATTERY = 2;
+ TEMPERATURE_TYPE_SKIN = 3;
+}
+
// Wakelock types, primarily used by android/os/PowerManager.java.
enum WakeLockLevelEnum {
// NOTE: Wake lock levels were previously defined as a bit field, except
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3b963d1..d6f3463 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1636,6 +1636,9 @@
<!-- Operating volatage for bluetooth controller. 0 by default-->
<integer translatable="false" name="config_bluetooth_operating_voltage_mv">0</integer>
+ <!-- Max number of connected audio devices supported by Bluetooth stack -->
+ <integer name="config_bluetooth_max_connected_audio_devices">1</integer>
+
<!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
<bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a6a3663..1babd70 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -405,6 +405,7 @@
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
+ <java-symbol type="integer" name="config_bluetooth_max_connected_audio_devices" />
<java-symbol type="integer" name="config_burnInProtectionMinHorizontalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMaxHorizontalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMinVerticalOffset" />
@@ -808,7 +809,6 @@
<java-symbol type="string" name="number_picker_increment_scroll_action" />
<java-symbol type="string" name="number_picker_increment_scroll_mode" />
<java-symbol type="string" name="old_app_action" />
- <java-symbol type="string" name="old_app_description" />
<java-symbol type="string" name="older" />
<java-symbol type="string" name="open_permission_deny" />
<java-symbol type="string" name="orgTypeCustom" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index be2c235..f4d4c44 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -524,6 +524,7 @@
Settings.Secure.NFC_PAYMENT_FOREGROUND,
Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
Settings.Secure.PACKAGE_VERIFIER_STATE,
Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index b135025..ba9b963 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -38,10 +38,10 @@
public void testStandardActions_serializationFlagIsValid() {
AccessibilityAction brokenStandardAction = CollectionUtils.find(
new ArrayList<>(AccessibilityAction.sStandardActions),
- action -> Integer.bitCount(action.mSerializationFlag) != 1);
+ action -> Long.bitCount(action.mSerializationFlag) != 1);
if (brokenStandardAction != null) {
String message = "Invalid serialization flag(0x"
- + Integer.toHexString(brokenStandardAction.mSerializationFlag)
+ + Long.toHexString(brokenStandardAction.mSerializationFlag)
+ ") in " + brokenStandardAction;
if (brokenStandardAction.mSerializationFlag == 0L) {
message += "\nThis is likely due to an overflow";
@@ -56,7 +56,7 @@
&& action.getId() != action.mSerializationFlag);
if (brokenStandardAction != null) {
fail("Serialization flag(0x"
- + Integer.toHexString(brokenStandardAction.mSerializationFlag)
+ + Long.toHexString(brokenStandardAction.mSerializationFlag)
+ ") is different from legacy action id(0x"
+ Integer.toHexString(brokenStandardAction.getId())
+ ") in " + brokenStandardAction);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 449e374..a15d337 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -398,6 +398,17 @@
}
@Test
+ public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash()
+ throws Exception {
+ configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+ configureValidShortcutService();
+ when(mServiceInfo.loadSummary(any())).thenReturn(null);
+ Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+ getController().performAccessibilityShortcut();
+ verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ }
+
+ @Test
public void testOnAccessibilityShortcut_forFrameworkFeature_callsServiceWithNoToast()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 8372331..778e768 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -94,12 +94,20 @@
// Grab a copy of everything we need
CanvasContext* context = mContext;
+ std::function<void(int64_t)> callback = std::move(mFrameCallback);
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
unblockUiThread();
}
+ // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
+ if (CC_UNLIKELY(callback)) {
+ context->enqueueFrameWork([callback, frameNr = context->getFrameNumber()]() {
+ callback(frameNr);
+ });
+ }
+
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
} else {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ea51ae4..d8c43e0 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -74,6 +74,10 @@
void run();
+ void setFrameCallback(std::function<void(int64_t)>&& callback) {
+ mFrameCallback = std::move(callback);
+ }
+
private:
void postAndWait();
bool syncFrameState(TreeInfo& info);
@@ -96,6 +100,8 @@
int64_t mSyncQueued;
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
+
+ std::function<void(int64_t)> mFrameCallback;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 79e46ed..4be7a57 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -270,6 +270,10 @@
mDrawFrameTask.setContentDrawBounds(left, top, right, bottom);
}
+void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
+ mDrawFrameTask.setFrameCallback(std::move(callback));
+}
+
void RenderProxy::serializeDisplayListTree() {
mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); });
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bc57d92..3425c5c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -115,6 +115,7 @@
ANDROID_API void removeRenderNode(RenderNode* node);
ANDROID_API void drawRenderNode(RenderNode* node);
ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
+ ANDROID_API void setFrameCallback(std::function<void(int64_t)>&& callback);
ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 3f9a4ef..dea38a8 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -31,7 +31,6 @@
/**
* Browses media content offered by a {@link MediaLibraryService2}.
- * @hide
*/
public class MediaBrowser2 extends MediaController2 {
// Equals to the ((MediaBrowser2Provider) getProvider())
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 6682e08..0114240 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,13 +19,13 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.ErrorCode;
import android.media.MediaSession2.PlaylistParams;
import android.media.session.MediaSessionManager;
import android.media.update.ApiLoader;
@@ -64,9 +64,8 @@
* <p>
* @see MediaSession2
* @see MediaSessionService2
- * @hide
*/
-public class MediaController2 implements AutoCloseable {
+public class MediaController2 implements AutoCloseable, MediaPlaylistController {
/**
* Interface for listening to change in activeness of the {@link MediaSession2}. It's
* active if and only if it has set a player.
@@ -126,19 +125,69 @@
/**
* Called when the playlist is changed.
+ * <p>
+ * When it's called, you should invalidate previous playback information such as position,
+ * player state, current item, etc.
*
* @param playlist A new playlist set by the session.
*/
+ // TODO(jaewan): Enhance doc
public void onPlaylistChanged(@NonNull List<MediaItem2> playlist) { }
/**
* Called when the playback state is changed.
*
* @param state latest playback state
+ * @hide
*/
+ // TODo(jaewan): Remove
public void onPlaybackStateChanged(@NonNull PlaybackState2 state) { }
/**
+ * Called when the player state is changed.
+ *
+ * @param state
+ */
+ public void onPlayerStateChanged(int state) { }
+
+ /**
+ * Called when the player's position is changed
+ *
+ * @param updateTimeMs timestamp when the position information is sent from the session
+ * @param positionMs position in millis
+ */
+ public void onPositionUpdated(long updateTimeMs, long positionMs) { }
+
+ /**
+ * Called when playback speed is changed.
+ *
+ * @param speed speed
+ */
+ public void onPlaybackSpeedChanged(float speed) { }
+
+ /**
+ * Called when the player's buffering position
+ *
+ * @param positionMs buffering position in millis
+ */
+ public void onBufferedPositionChanged(long positionMs) { }
+
+ /**
+ * Called when a error from
+ *
+ * @param errorCode error code
+ * @param extra extra information
+ */
+ public void onError(@ErrorCode int errorCode, int extra) { }
+
+ /**
+ * Called when the player's current playing item is changed
+ *
+ * @param item new item
+ */
+ public void onCurrentPlaylistItemChanged(MediaItem2 item) { }
+
+ /**
* Called when the playlist parameters are changed.
*
* @param params The new play list parameters.
@@ -166,7 +215,6 @@
/**
* @hide
*/
- @SystemApi
public PlaybackInfo(PlaybackInfoProvider provider) {
mProvider = provider;
}
@@ -174,7 +222,6 @@
/**
* @hide
*/
- @SystemApi
public PlaybackInfoProvider getProvider() {
return mProvider;
}
@@ -281,7 +328,6 @@
/**
* @hide
*/
- @SystemApi
public MediaController2Provider getProvider() {
return mProvider;
}
@@ -325,7 +371,7 @@
* Request that the player prepare its playback. In other words, other sessions can continue
* to play during the preparation of this session. This method can be used to speed up the
* start of the playback. Once the preparation is done, the session will change its playback
- * state to {@link PlaybackState2#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+ * state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards, {@link #play} can be called to
* start playback.
*/
public void prepare() {
@@ -360,12 +406,13 @@
/**
* Sets the index of current DataSourceDesc in the play list to be played.
*
- * @param index the index of DataSourceDesc in the play list you want to play
+ * @param item the index of DataSourceDesc in the play list you want to play
* @throws IllegalArgumentException if the play list is null
* @throws NullPointerException if index is outside play list range
*/
- public void setCurrentPlaylistItem(int index) {
- mProvider.setCurrentPlaylistItem_impl(index);
+ @Override
+ public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+ mProvider.skipToPlaylistItem_impl(item);
}
/**
@@ -375,7 +422,7 @@
* @param params A {@link PlaylistParams} object to set.
* @throws IllegalArgumentException if given {@param param} is null.
*/
- public void setPlaylistParams(PlaylistParams params) {
+ public void setPlaylistParams(@NonNull PlaylistParams params) {
mProvider.setPlaylistParams_impl(params);
}
@@ -428,12 +475,11 @@
mProvider.playFromUri_impl(uri, extras);
}
-
/**
* Request that the player prepare playback for a specific media id. In other words, other
* sessions can continue to play during the preparation of this session. This method can be
* used to speed up the start of the playback. Once the preparation is done, the session
- * will change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+ * will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromMediaId} can be directly called without this method.
*
@@ -450,7 +496,7 @@
* query should be treated as a request to prepare any music. In other words, other sessions
* can continue to play during the preparation of this session. This method can be used to
* speed up the start of the playback. Once the preparation is done, the session will
- * change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+ * change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromSearch} can be directly called without this method.
*
@@ -466,7 +512,7 @@
* Request that the player prepare playback for a specific {@link Uri}. In other words,
* other sessions can continue to play during the preparation of this session. This method
* can be used to speed up the start of the playback. Once the preparation is done, the
- * session will change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+ * session will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromUri} can be directly called without this method.
*
@@ -537,12 +583,65 @@
* playback state.
*
* @return a playback state. Can be {@code null}
+ * @hide
*/
public @Nullable PlaybackState2 getPlaybackState() {
return mProvider.getPlaybackState_impl();
}
/**
+ * Get the lastly cached player state from {@link ControllerCallback#onPlayerStateChanged(int)}.
+ *
+ * @return player state
+ */
+ public int getPlayerState() {
+ return mProvider.getPlayerState_impl();
+ }
+
+ /**
+ * Get the lastly cached position from {@link ControllerCallback#onPositionUpdated(long, long)}.
+ * <p>
+ * This returns the calculated value of the position, based on the difference between the
+ * update time and current time.
+ *
+ * @return position
+ */
+ public long getPosition() {
+ return mProvider.getPosition_impl();
+ }
+
+ /**
+ * Get the lastly cached playback speed from
+ * {@link ControllerCallback#onPlaybackSpeedChanged(float)}.
+ *
+ * @return speed
+ */
+ public float getPlaybackSpeed() {
+ return mProvider.getPlaybackSpeed_impl();
+ }
+
+ /**
+ * Get the lastly cached buffered position from
+ * {@link ControllerCallback#onBufferedPositionChanged(long)}.
+ *
+ * @return buffering position in millis
+ */
+ public long getBufferedPosition() {
+ return mProvider.getBufferedPosition_impl();
+ }
+
+ /**
+ * Get the lastly cached current item from
+ * {@link ControllerCallback#onCurrentPlaylistItemChanged(MediaItem2)}.
+ *
+ * @return index of the current item
+ */
+ @Override
+ public MediaItem2 getCurrentPlaylistItem() {
+ return mProvider.getCurrentPlaylistItem_impl();
+ }
+
+ /**
* Get the current playback info for this session.
*
* @return The current playback info or null.
@@ -584,6 +683,7 @@
*
* @return playlist. Can be {@code null} if the controller doesn't have enough permission.
*/
+ @Override
public @Nullable List<MediaItem2> getPlaylist() {
return mProvider.getPlaylist_impl();
}
@@ -603,13 +703,11 @@
* If index is same as the current index of the playlist, current playback
* will be stopped and playback moves to next source in the list.
*
- * @return the removed DataSourceDesc at index in the play list
* @throws IllegalArgumentException if the play list is null
* @throws IndexOutOfBoundsException if index is outside play list range
*/
- // TODO(jaewan): Remove with index was previously rejected by council (b/36524925)
- // TODO(jaewan): Should we also add movePlaylistItem from index to index?
- public void removePlaylistItem(MediaItem2 item) {
+ @Override
+ public void removePlaylistItem(@NonNull MediaItem2 item) {
mProvider.removePlaylistItem_impl(item);
}
@@ -620,12 +718,12 @@
* If index is less than or equal to the current index of the play list,
* the current index of the play list will be incremented correspondingly.
*
- * @param index the index you want to add dsd to the play list
- * @param item the media item you want to add to the play list
+ * @param index the index you want to add
+ * @param item the media item you want to add
* @throws IndexOutOfBoundsException if index is outside play list range
- * @throws NullPointerException if dsd is null
*/
- public void addPlaylistItem(int index, MediaItem2 item) {
+ @Override
+ public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
mProvider.addPlaylistItem_impl(index, item);
}
}
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 667aac1..b7b75e4 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.MediaItem2Provider;
@@ -35,7 +34,6 @@
* When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
* <p>
* This object isn't a thread safe.
- * @hide
*/
public class MediaItem2 {
/** @hide */
@@ -64,7 +62,9 @@
* @param mediaId id of this item. It must be unique whithin this app
* @param metadata metadata with the media id.
* @param flags The flags for this item.
+ * @hide
*/
+ // TODO(jaewan): Remove this
public MediaItem2(@NonNull Context context, @NonNull String mediaId,
@NonNull DataSourceDesc dsd, @Nullable MediaMetadata2 metadata,
@Flags int flags) {
@@ -76,7 +76,6 @@
* Create a new media item
* @hide
*/
- @SystemApi
public MediaItem2(MediaItem2Provider provider) {
mProvider = provider;
}
@@ -156,4 +155,87 @@
public @Nullable DataSourceDesc getDataSourceDesc() {
return mProvider.getDataSourceDesc_impl();
}
+
+ /**
+ * Build {@link MediaItem2}
+ */
+ // TODO(jaewan): Move it to updatable
+ public static final class Builder {
+ private Context mContext;
+ private @Flags int mFlags;
+ private String mMediaId;
+ private MediaMetadata2 mMetadata;
+ private DataSourceDesc mDataSourceDesc;
+
+ /**
+ * Constructor for {@link Builder}
+ *
+ * @param context
+ * @param flags
+ */
+ public Builder(@NonNull Context context, @Flags int flags) {
+ mContext = context;
+ mFlags = flags;
+ }
+
+ /**
+ * Set the media id of this instance. {@code null} for unset.
+ * <p>
+ * Media id is used to identify a media contents between session and controller.
+ * <p>
+ * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+ * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+ * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+ * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+ *
+ * @param mediaId media id
+ * @return this instance for chaining
+ */
+ public Builder setMediaId(@Nullable String mediaId) {
+ mMediaId = mediaId;
+ return this;
+ }
+
+ /**
+ * Set the metadata of this instance. {@code null} for unset.
+ * <p>
+ * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+ * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+ * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+ * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+ *
+ * @param metadata metadata
+ * @return this instance for chaining
+ */
+ public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
+ mMetadata = metadata;
+ return this;
+ }
+
+ /**
+ * Set the data source descriptor for this instance. {@code null} for unset.
+ *
+ * @param dataSourceDesc data source descriptor
+ * @return this instance for chaining
+ */
+ public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
+ mDataSourceDesc = dataSourceDesc;
+ return this;
+ }
+
+ /**
+ * Build {@link MediaItem2}.
+ *
+ * @return a new {@link MediaItem2}.
+ */
+ public MediaItem2 build() {
+ String id = (mMetadata != null)
+ ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null;
+ if (id == null) {
+ // TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?)
+ id = (mMediaId != null) ? mMediaId : toString();
+ }
+ return new MediaItem2(mContext, id, mDataSourceDesc, mMetadata, mFlags);
+ }
+ }
}
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 11b745a..768d044 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -19,10 +19,10 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
-import android.media.MediaSession2.BuilderBase;
+import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -40,7 +40,7 @@
* and ask the application to start playing it. They may also be used to control content that
* is already playing by way of a {@link MediaSession2}.
* <p>
- * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * When extending this class, also add the following to your {@code AndroidManifest.xml}.
* <pre>
* <service android:name="component_name_of_your_implementation" >
* <intent-filter>
@@ -48,11 +48,12 @@
* </intent-filter>
* </service></pre>
* <p>
- * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
+ * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't
* be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
* default, an empty string will be used for ID of the service. If you want to specify an ID,
* declare metadata in the manifest as follows.
- * @hide
+ *
+ * @see MediaSessionService2
*/
public abstract class MediaLibraryService2 extends MediaSessionService2 {
/**
@@ -63,15 +64,178 @@
/**
* Session for the {@link MediaLibraryService2}. Build this object with
- * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}.
+ * {@link Builder} and return in {@link #onCreateSession(String)}.
*/
- public static class MediaLibrarySession extends MediaSession2 {
+ public static final class MediaLibrarySession extends MediaSession2 {
private final MediaLibrarySessionProvider mProvider;
/**
+ * Callback for the {@link MediaLibrarySession}.
+ */
+ public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+ public MediaLibrarySessionCallback(Context context) {
+ super(context);
+ }
+
+ /**
+ * Called to get the root information for browsing by a particular client.
+ * <p>
+ * The implementation should verify that the client package has permission
+ * to access browse media information before returning the root id; it
+ * should return null if the client is not allowed to access this
+ * information.
+ *
+ * @param controllerInfo information of the controller requesting access to browse media.
+ * @param rootHints An optional bundle of service-specific arguments to send
+ * to the media library service when connecting and retrieving the
+ * root id for browsing, or null if none. The contents of this
+ * bundle may affect the information returned when browsing.
+ * @return The {@link LibraryRoot} for accessing this app's content or null.
+ * @see LibraryRoot#EXTRA_RECENT
+ * @see LibraryRoot#EXTRA_OFFLINE
+ * @see LibraryRoot#EXTRA_SUGGESTED
+ */
+ public @Nullable LibraryRoot onGetLibraryRoot(@NonNull ControllerInfo controllerInfo,
+ @Nullable Bundle rootHints) {
+ return null;
+ }
+
+ /**
+ * Called to get an item. Return result here for the browser.
+ * <p>
+ * Return {@code null} for no result or error.
+ *
+ * @param mediaId item id to get media item.
+ * @return a media item. {@code null} for no result or error.
+ */
+ public @Nullable MediaItem2 onGetItem(@NonNull ControllerInfo controllerInfo,
+ @NonNull String mediaId) {
+ return null;
+ }
+
+ /**
+ * Called to get children of given parent id. Return the children here for the browser.
+ * <p>
+ * Return an empty list for no children, and return {@code null} for the error.
+ *
+ * @param parentId parent id to get children
+ * @param page number of page
+ * @param pageSize size of the page
+ * @param extras extra bundle
+ * @return list of children. Can be {@code null}.
+ */
+ public @Nullable List<MediaItem2> onGetChildren(@NonNull ControllerInfo controller,
+ @NonNull String parentId, int page, int pageSize, @Nullable Bundle extras) {
+ return null;
+ }
+
+ /**
+ * Called when a controller subscribes to the parent.
+ * <p>
+ * It's your responsibility to keep subscriptions by your own and call
+ * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
+ * when the parent is changed.
+ *
+ * @param controller controller
+ * @param parentId parent id
+ * @param extras extra bundle
+ */
+ public void onSubscribe(@NonNull ControllerInfo controller, @NonNull String parentId,
+ @Nullable Bundle extras) {
+ }
+
+ /**
+ * Called when a controller unsubscribes to the parent.
+ *
+ * @param controller controller
+ * @param parentId parent id
+ */
+ public void onUnsubscribe(@NonNull ControllerInfo controller,
+ @NonNull String parentId) {
+ }
+
+ /**
+ * Called when a controller requests search.
+ *
+ * @param query The search query sent from the media browser. It contains keywords
+ * separated by space.
+ * @param extras The bundle of service-specific arguments sent from the media browser.
+ */
+ public void onSearch(@NonNull ControllerInfo controllerInfo, @NonNull String query,
+ @Nullable Bundle extras) {
+ }
+
+ /**
+ * Called to get the search result. Return search result here for the browser which has
+ * requested search previously.
+ * <p>
+ * Return an empty list for no search result, and return {@code null} for the error.
+ *
+ * @param controllerInfo Information of the controller requesting the search result.
+ * @param query The search query which was previously sent through
+ * {@link #onSearch(ControllerInfo, String, Bundle)} call.
+ * @param page page number. Starts from {@code 1}.
+ * @param pageSize page size. Should be greater or equal to {@code 1}.
+ * @param extras The bundle of service-specific arguments sent from the media browser.
+ * @return search result. {@code null} for error.
+ */
+ public @Nullable List<MediaItem2> onGetSearchResult(
+ @NonNull ControllerInfo controllerInfo, @NonNull String query, int page,
+ int pageSize, @Nullable Bundle extras) {
+ return null;
+ }
+ }
+
+ /**
+ * Builder for {@link MediaLibrarySession}.
+ */
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
+ public static final class Builder extends BuilderBase<MediaLibrarySession, Builder,
+ MediaLibrarySessionCallback> {
+ // Builder requires MediaLibraryService2 instead of Context just to ensure that the
+ // builder can be only instantiated within the MediaLibraryService2.
+ // Ideally it's better to make it inner class of service to enforce, it violates API
+ // guideline that Builders should be the inner class of the building target.
+ public Builder(@NonNull MediaLibraryService2 service,
+ @NonNull MediaPlayerBase player,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull MediaLibrarySessionCallback callback) {
+ super((instance) -> ApiLoader.getProvider(service)
+ .createMediaLibraryService2Builder(service, (Builder) instance, player,
+ callbackExecutor, callback));
+ }
+
+ @Override
+ public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public Builder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public Builder setId(String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public Builder setSessionCallback(@NonNull Executor executor,
+ @NonNull MediaLibrarySessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaLibrarySession build() {
+ return super.build();
+ }
+ }
+
+ /**
* @hide
*/
- @SystemApi
public MediaLibrarySession(MediaLibrarySessionProvider provider) {
super(provider);
mProvider = provider;
@@ -124,166 +288,6 @@
}
}
- /**
- * Callback for the {@link MediaLibrarySession}.
- */
- public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
-
- public MediaLibrarySessionCallback(Context context) {
- super(context);
- }
-
- /**
- * Called to get the root information for browsing by a particular client.
- * <p>
- * The implementation should verify that the client package has permission
- * to access browse media information before returning the root id; it
- * should return null if the client is not allowed to access this
- * information.
- *
- * @param controllerInfo information of the controller requesting access to browse media.
- * @param rootHints An optional bundle of service-specific arguments to send
- * to the media library service when connecting and retrieving the
- * root id for browsing, or null if none. The contents of this
- * bundle may affect the information returned when browsing.
- * @return The {@link LibraryRoot} for accessing this app's content or null.
- * @see LibraryRoot#EXTRA_RECENT
- * @see LibraryRoot#EXTRA_OFFLINE
- * @see LibraryRoot#EXTRA_SUGGESTED
- */
- public @Nullable LibraryRoot onGetLibraryRoot(@NonNull ControllerInfo controllerInfo,
- @Nullable Bundle rootHints) {
- return null;
- }
-
- /**
- * Called to get an item. Return result here for the browser.
- * <p>
- * Return {@code null} for no result or error.
- *
- * @param mediaId item id to get media item.
- * @return a media item. {@code null} for no result or error.
- */
- public @Nullable MediaItem2 onGetItem(@NonNull ControllerInfo controllerInfo,
- @NonNull String mediaId) {
- return null;
- }
-
- /**
- * Called to get children of given parent id. Return the children here for the browser.
- * <p>
- * Return an empty list for no children, and return {@code null} for the error.
- *
- * @param parentId parent id to get children
- * @param page number of page
- * @param pageSize size of the page
- * @param extras extra bundle
- * @return list of children. Can be {@code null}.
- */
- public @Nullable List<MediaItem2> onGetChildren(@NonNull ControllerInfo controller,
- @NonNull String parentId, int page, int pageSize, @Nullable Bundle extras) {
- return null;
- }
-
- /**
- * Called when a controller subscribes to the parent.
- * <p>
- * It's your responsibility to keep subscriptions by your own and call
- * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
- * when the parent is changed.
- *
- * @param controller controller
- * @param parentId parent id
- * @param extras extra bundle
- */
- public void onSubscribe(@NonNull ControllerInfo controller, @NonNull String parentId,
- @Nullable Bundle extras) {
- }
-
- /**
- * Called when a controller unsubscribes to the parent.
- *
- * @param controller controller
- * @param parentId parent id
- */
- public void onUnsubscribe(@NonNull ControllerInfo controller, @NonNull String parentId) {
- }
-
- /**
- * Called when a controller requests search.
- *
- * @param query The search query sent from the media browser. It contains keywords separated
- * by space.
- * @param extras The bundle of service-specific arguments sent from the media browser.
- */
- public void onSearch(@NonNull ControllerInfo controllerInfo, @NonNull String query,
- @Nullable Bundle extras) {
- }
-
- /**
- * Called to get the search result. Return search result here for the browser which has
- * requested search previously.
- * <p>
- * Return an empty list for no search result, and return {@code null} for the error.
- *
- * @param controllerInfo Information of the controller requesting the search result.
- * @param query The search query which was previously sent through
- * {@link #onSearch(ControllerInfo, String, Bundle)} call.
- * @param page page number. Starts from {@code 1}.
- * @param pageSize page size. Should be greater or equal to {@code 1}.
- * @param extras The bundle of service-specific arguments sent from the media browser.
- * @return search result. {@code null} for error.
- */
- public @Nullable List<MediaItem2> onGetSearchResult(@NonNull ControllerInfo controllerInfo,
- @NonNull String query, int page, int pageSize, @Nullable Bundle extras) {
- return null;
- }
- }
-
- /**
- * Builder for {@link MediaLibrarySession}.
- */
- // Override all methods just to show them with the type instead of generics in Javadoc.
- // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
- public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
- MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
- public MediaLibrarySessionBuilder(
- @NonNull Context context, @NonNull MediaPlayerInterface player,
- @NonNull @CallbackExecutor Executor callbackExecutor,
- @NonNull MediaLibrarySessionCallback callback) {
- super((instance) -> ApiLoader.getProvider(context).createMediaLibraryService2Builder(
- context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
- callback));
- }
-
- @Override
- public MediaLibrarySessionBuilder setVolumeProvider(
- @Nullable VolumeProvider2 volumeProvider) {
- return super.setVolumeProvider(volumeProvider);
- }
-
- @Override
- public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
- return super.setSessionActivity(pi);
- }
-
- @Override
- public MediaLibrarySessionBuilder setId(String id) {
- return super.setId(id);
- }
-
- @Override
- public MediaLibrarySessionBuilder setSessionCallback(
- @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) {
- return super.setSessionCallback(executor, callback);
- }
-
- @Override
- public MediaLibrarySession build() {
- return super.build();
- }
- }
-
@Override
MediaSessionService2Provider createProvider() {
return ApiLoader.getProvider(this).createMediaLibraryService2(this);
@@ -302,7 +306,7 @@
*
* @param sessionId session id written in the AndroidManifest.xml.
* @return a new library session
- * @see MediaLibrarySessionBuilder
+ * @see Builder
* @see #getSession()
* @throws RuntimeException if returned session is invalid
*/
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 1f856bc..b363831 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.SystemApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.update.ApiLoader;
@@ -33,15 +32,13 @@
/**
* Contains metadata about an item, such as the title, artist, etc.
- *
- * @hide
*/
+// New version of MediaMetadata with following changes
+// - Don't implement Parcelable for updatable support.
+// - Also support MediaDescription features. MediaDescription is deprecated instead because
+// it was insufficient for controller to display media contents.
+// TODO(jaewan): Add @see for APIs from MediaDescription
public final class MediaMetadata2 {
- // New version of MediaMetadata that no longer implements Parcelable but added from/toBundle()
- // for updatable.
- // MediaDescription is deprecated because it was insufficient for controller to display media
- // contents. Added getExtra() here to support all the features from the MediaDescription.
-
/**
* The title of the media.
*/
@@ -389,7 +386,6 @@
/**
* @hide
*/
- @SystemApi
public MediaMetadata2(MediaMetadata2Provider provider) {
mProvider = provider;
}
@@ -568,7 +564,6 @@
/**
* @hide
*/
- @SystemApi
public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
mProvider = provider;
}
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
new file mode 100644
index 0000000..3181362
--- /dev/null
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.MediaSession2.PlaylistParams;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Base class for all media players that want media session.
+ */
+public abstract class MediaPlayerBase implements AutoCloseable {
+ /**
+ * @hide
+ */
+ @IntDef({STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ /**
+ * State when the player is idle, and needs configuration to start playback.
+ */
+ public static final int STATE_IDLE = 0;
+
+ /**
+ * State when the player's playback is paused
+ */
+ public static final int STATE_PAUSED = 0;
+
+ /**
+ * State when the player's playback is ongoing
+ */
+ public static final int STATE_PLAYING = 0;
+
+ /**
+ * State when the player is in error state and cannot be recovered self.
+ */
+ public static final int STATE_ERROR = 0;
+
+ /**
+ * Unspecified media player error.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
+
+ /**
+ * The video is streamed and its container is not valid for progressive
+ * playback i.e the video's index (e.g moov atom) is not at the start of the
+ * file.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
+ MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
+
+ /**
+ * File or network related operation errors.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
+
+ /**
+ * Bitstream is not conforming to the related coding standard or file spec.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
+
+ /**
+ * Bitstream is conforming to the related coding standard or file spec, but
+ * the media framework does not support the feature.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
+ /**
+ * Some operation takes too long to complete, usually more than 3-5 seconds.
+ * @hide
+ */
+ public static final int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
+
+ /**
+ * Callbacks to listens to the changes in {@link PlaybackState2} and error.
+ * @hide
+ */
+ public static abstract class EventCallback {
+ /**
+ * Called when {@link PlaybackState2} for this player is changed.
+ */
+ public void onPlaybackStateChanged(PlaybackState2 state) { }
+
+ /**
+ * Called to indicate an error.
+ *
+ * @param mediaId optional mediaId to indicate error
+ * @param what what
+ * @param extra
+ */
+ public void onError(@Nullable String mediaId, int what, int extra) { }
+ }
+
+ // Transport controls that session will send command directly to this player.
+ /**
+ * Start or resumes playback
+ */
+ public abstract void play();
+
+ /**
+ * @hide
+ */
+ public abstract void prepare();
+
+ /**
+ * Pause playback
+ */
+ public abstract void pause();
+
+ /**
+ * @hide
+ */
+ public abstract void stop();
+
+ /**
+ * @hide
+ */
+ public abstract void skipToPrevious();
+
+ /**
+ * @hide
+ */
+ public abstract void skipToNext();
+
+ /**
+ * @hide
+ */
+ public abstract void seekTo(long pos);
+
+ /**
+ * @hide
+ */
+ public abstract void fastForward();
+
+ /**
+ * @hide
+ */
+ public abstract void rewind();
+
+ /**
+ * @hide
+ */
+ public abstract PlaybackState2 getPlaybackState();
+
+ /**
+ * Return player state.
+ *
+ * @return player state
+ * @see #STATE_IDLE
+ * @see #STATE_PLAYING
+ * @see #STATE_PAUSED
+ * @see #STATE_ERROR
+ */
+ public abstract @State int getPlayerState();
+
+ /**
+ * Sets the {@link AudioAttributes} to be used during the playback of the media.
+ *
+ * @param attributes non-null <code>AudioAttributes</code>.
+ */
+ public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+
+ /**
+ * Returns AudioAttributes that media player has.
+ */
+ public abstract @Nullable AudioAttributes getAudioAttributes();
+
+ /**
+ * @hide
+ */
+ public abstract void addPlaylistItem(int index, MediaItem2 item);
+
+ /**
+ * @hide
+ */
+ public abstract void removePlaylistItem(MediaItem2 item);
+
+ /**
+ * @hide
+ */
+ public abstract void setPlaylist(List<MediaItem2> playlist);
+
+ /**
+ * @hide
+ */
+ public abstract List<MediaItem2> getPlaylist();
+
+ /**
+ * @hide
+ */
+ public abstract void setCurrentPlaylistItem(MediaItem2 item);
+
+ /**
+ * @hide
+ */
+ public abstract void setPlaylistParams(PlaylistParams params);
+
+ /**
+ * @hide
+ */
+ public abstract PlaylistParams getPlaylistParams();
+
+ /**
+ * Register a {@link EventCallback}.
+ *
+ * @param executor a callback executor
+ * @param callback a EventCallback
+ * @hide
+ */
+ public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull EventCallback callback);
+
+ /**
+ * Unregister previously registered {@link EventCallback}.
+ *
+ * @param callback a EventCallback
+ * @hide
+ */
+ public abstract void unregisterEventCallback(@NonNull EventCallback callback);
+}
diff --git a/media/java/android/media/MediaPlayerInterface.java b/media/java/android/media/MediaPlayerInterface.java
deleted file mode 100644
index b81c3d6..0000000
--- a/media/java/android/media/MediaPlayerInterface.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.MediaSession2.PlaylistParams;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Base interfaces for all media players that want media session.
- * @hide
- */
-public interface MediaPlayerInterface {
- /**
- * Unspecified media player error.
- */
- int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
-
- /**
- * The video is streamed and its container is not valid for progressive
- * playback i.e the video's index (e.g moov atom) is not at the start of the
- * file.
- */
- int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
- MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
-
- /**
- * File or network related operation errors.
- */
- int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
-
- /**
- * Bitstream is not conforming to the related coding standard or file spec.
- */
- int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
-
- /**
- * Bitstream is conforming to the related coding standard or file spec, but
- * the media framework does not support the feature.
- */
- int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
- /**
- * Some operation takes too long to complete, usually more than 3-5 seconds.
- */
- int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
-
- /**
- * Callbacks to listens to the changes in {@link PlaybackState2} and error.
- */
- interface EventCallback {
- /**
- * Called when {@link PlaybackState2} for this player is changed.
- */
- default void onPlaybackStateChanged(PlaybackState2 state) { }
-
- /**
- * Called to indicate an error.
- *
- * @param mediaId optional mediaId to indicate error
- * @param what what
- * @param extra
- */
- default void onError(@Nullable String mediaId, int what, int extra) { }
- }
-
- // Transport controls that session will send command directly to this player.
- void play();
- void prepare();
- void pause();
- void stop();
- void skipToPrevious();
- void skipToNext();
- void seekTo(long pos);
- void fastForward();
- void rewind();
-
- PlaybackState2 getPlaybackState();
-
- /**
- * Sets the {@link AudioAttributes} to be used during the playback of the media.
- *
- * @param attributes non-null <code>AudioAttributes</code>.
- */
- void setAudioAttributes(@NonNull AudioAttributes attributes);
-
- /**
- * Returns AudioAttributes that media player has.
- */
- @Nullable
- AudioAttributes getAudioAttributes();
-
- void addPlaylistItem(int index, MediaItem2 item);
- void removePlaylistItem(MediaItem2 item);
-
- void setPlaylist(List<MediaItem2> playlist);
- List<MediaItem2> getPlaylist();
-
- void setCurrentPlaylistItem(int index);
- void setPlaylistParams(PlaylistParams params);
- PlaylistParams getPlaylistParams();
-
- /**
- * Register a {@link EventCallback}.
- *
- * @param executor a callback executor
- * @param callback a EventCallback
- */
- void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback callback);
-
- /**
- * Unregister previously registered {@link EventCallback}.
- *
- * @param callback a EventCallback
- */
- void unregisterEventCallback(@NonNull EventCallback callback);
-}
diff --git a/media/java/android/media/MediaPlaylistController.java b/media/java/android/media/MediaPlaylistController.java
new file mode 100644
index 0000000..916c12a
--- /dev/null
+++ b/media/java/android/media/MediaPlaylistController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+
+import java.util.List;
+
+/**
+ * Controller interfaces for playlist management for both {@link MediaSession2} and
+ * {@link MediaController2} that related with metadata. This ensures that two classes share the same
+ * interface.
+ * <p>
+ * This class only includes methods that involves {@link MediaItem2}. Because other APIs are
+ * considered as the part of {@link MediaPlayerBase} (e.g. set/getPlaylistParams()}. Note that
+ * setPlaylist() isn't added on purpose because it's considered as session specific.
+ *
+ * @hide
+ */
+public interface MediaPlaylistController {
+ // TODO(jaewan): is Index correct here?
+ void addPlaylistItem(int index, @NonNull MediaItem2 item);
+ void removePlaylistItem(@NonNull MediaItem2 item);
+ MediaItem2 getCurrentPlaylistItem();
+ void skipToPlaylistItem(@NonNull MediaItem2 item);
+ List<MediaItem2> getPlaylist();
+}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 63e4e65..54b1f0e 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -20,11 +20,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.EventCallback;
+import android.media.MediaPlayerBase.EventCallback;
import android.media.session.MediaSession;
import android.media.session.MediaSession.Callback;
import android.media.session.PlaybackState;
@@ -70,7 +69,7 @@
* <p>
* When a session receive transport control commands, the session sends the commands directly to
* the the underlying media player set by {@link Builder} or
- * {@link #setPlayer(MediaPlayerInterface)}.
+ * {@link #setPlayer(MediaPlayerBase)}.
* <p>
* When an app is finished performing playback it must call {@link #close()} to clean up the session
* and notify any controllers.
@@ -78,9 +77,8 @@
* {@link MediaSession2} objects should be used on the thread on the looper.
*
* @see MediaSessionService2
- * @hide
*/
-public class MediaSession2 implements AutoCloseable {
+public class MediaSession2 implements AutoCloseable, MediaPlaylistController {
private final MediaSession2Provider mProvider;
// TODO(jaewan): Should we define IntDef? Currently we don't have to allow subclass to add more.
@@ -143,7 +141,7 @@
public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
/**
- * Command code for {@link MediaController2#fastForward()} ()}.
+ * Command code for {@link MediaController2#fastForward()}.
* <p>
* This is transport control command. Command would be sent directly to the player if the
* session doesn't reject the request through the
@@ -160,14 +158,14 @@
public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
/**
- * Command code for {@link MediaController2#seekTo(long)} ()}.
+ * Command code for {@link MediaController2#seekTo(long)}.
* <p>
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
*/
public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
/**
- * Command code for {@link MediaController2#setCurrentPlaylistItem(int)} ()}.
+ * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
* <p>
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
@@ -222,7 +220,7 @@
public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16;
/**
- * Command code for {@link MediaController2#playFromUri(String, Bundle)}.
+ * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
*/
public static final int COMMAND_CODE_PLAY_FROM_URI = 17;
@@ -256,6 +254,84 @@
public static final int COMMAND_CODE_BROWSER = 22;
/**
+ * @hide
+ */
+ @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
+ ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
+ ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
+ ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
+ ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE,
+ ERROR_CODE_SETUP_REQUIRED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ /**
+ * This is the default error code and indicates that none of the other error codes applies.
+ */
+ public static final int ERROR_CODE_UNKNOWN_ERROR = 0;
+
+ /**
+ * Error code when the application state is invalid to fulfill the request.
+ */
+ public static final int ERROR_CODE_APP_ERROR = 1;
+
+ /**
+ * Error code when the request is not supported by the application.
+ */
+ public static final int ERROR_CODE_NOT_SUPPORTED = 2;
+
+ /**
+ * Error code when the request cannot be performed because authentication has expired.
+ */
+ public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3;
+
+ /**
+ * Error code when a premium account is required for the request to succeed.
+ */
+ public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4;
+
+ /**
+ * Error code when too many concurrent streams are detected.
+ */
+ public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5;
+
+ /**
+ * Error code when the content is blocked due to parental controls.
+ */
+ public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6;
+
+ /**
+ * Error code when the content is blocked due to being regionally unavailable.
+ */
+ public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7;
+
+ /**
+ * Error code when the requested content is already playing.
+ */
+ public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8;
+
+ /**
+ * Error code when the application cannot skip any more songs because skip limit is reached.
+ */
+ public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9;
+
+ /**
+ * Error code when the action is interrupted due to some external event.
+ */
+ public static final int ERROR_CODE_ACTION_ABORTED = 10;
+
+ /**
+ * Error code when the playback navigation (previous, next) is not possible because the queue
+ * was exhausted.
+ */
+ public static final int ERROR_CODE_END_OF_QUEUE = 11;
+
+ /**
+ * Error code when the session needs user's manual intervention.
+ */
+ public static final int ERROR_CODE_SETUP_REQUIRED = 12;
+
+ /**
* Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
* <p>
* If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
@@ -324,7 +400,7 @@
/**
* Represent set of {@link Command}.
*/
- public static class CommandGroup {
+ public static final class CommandGroup {
private final CommandGroupProvider mProvider;
public CommandGroup(Context context) {
@@ -360,7 +436,6 @@
/**
* @hide
*/
- @SystemApi
public CommandGroupProvider getProvider() {
return mProvider;
}
@@ -390,7 +465,7 @@
* default.
*/
// TODO(jaewan): Can we move this inside of the updatable for default implementation.
- public static class SessionCallback {
+ public static abstract class SessionCallback {
private final Context mContext;
public SessionCallback(Context context) {
@@ -587,7 +662,7 @@
/**
* Base builder class for MediaSession2 and its subclass. Any change in this class should be
* also applied to the subclasses {@link MediaSession2.Builder} and
- * {@link MediaLibraryService2.MediaLibrarySessionBuilder}.
+ * {@link MediaLibraryService2.MediaLibrarySession.Builder}.
* <p>
* APIs here should be package private, but should have documentations for developers.
* Otherwise, javadoc will generate documentation with the generic types such as follows.
@@ -684,7 +759,7 @@
// Override all methods just to show them with the type instead of generics in Javadoc.
// This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
- public Builder(Context context, @NonNull MediaPlayerInterface player) {
+ public Builder(Context context, @NonNull MediaPlayerBase player) {
super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
context, (Builder) instance, player));
}
@@ -725,7 +800,6 @@
/**
* @hide
*/
- // TODO(jaewan): SystemApi
// TODO(jaewan): Also accept componentName to check notificaiton listener.
public ControllerInfo(Context context, int uid, int pid, String packageName,
IInterface callback) {
@@ -762,7 +836,6 @@
/**
* @hide
*/
- @SystemApi
public ControllerInfoProvider getProvider() {
return mProvider;
}
@@ -794,13 +867,12 @@
* <p>
* It's up to the controller's decision to respect or ignore this customization request.
*/
- public static class CommandButton {
+ public static final class CommandButton {
private final CommandButtonProvider mProvider;
/**
* @hide
*/
- @SystemApi
public CommandButton(CommandButtonProvider provider) {
mProvider = provider;
}
@@ -856,7 +928,6 @@
/**
* @hide
*/
- @SystemApi
public CommandButtonProvider getProvider() {
return mProvider;
}
@@ -864,7 +935,7 @@
/**
* Builder for {@link CommandButton}.
*/
- public static class Builder {
+ public static final class Builder {
private final CommandButtonProvider.BuilderProvider mProvider;
public Builder(@NonNull Context context) {
@@ -1038,7 +1109,6 @@
* framework had to add heuristics to figure out if an app is
* @hide
*/
- @SystemApi
public MediaSession2(MediaSession2Provider provider) {
super();
mProvider = provider;
@@ -1047,39 +1117,34 @@
/**
* @hide
*/
- @SystemApi
public MediaSession2Provider getProvider() {
return mProvider;
}
/**
- * Set the underlying {@link MediaPlayerInterface} for this session to dispatch incoming event
+ * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
* to. Events from the {@link MediaController2} will be sent directly to the underlying
* player on the {@link Handler} where the session is created on.
* <p>
- * If the new player is successfully set,
- * {@link EventCallback#onPlaybackStateChanged(PlaybackState2)} will be called to tell the
- * current playback state of the new player.
- * <p>
* For the remote playback case which you want to handle volume by yourself, use
- * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
+ * {@link #setPlayer(MediaPlayerBase, VolumeProvider2)}.
*
- * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
+ * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
* @throws IllegalArgumentException if the player is {@code null}.
*/
- public void setPlayer(@NonNull MediaPlayerInterface player) {
+ public void setPlayer(@NonNull MediaPlayerBase player) {
mProvider.setPlayer_impl(player);
}
/**
- * Set the underlying {@link MediaPlayerInterface} with the volume provider for remote playback.
+ * Set the underlying {@link MediaPlayerBase} with the volume provider for remote playback.
*
- * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
+ * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
* @param volumeProvider a volume provider
- * @see #setPlayer(MediaPlayerInterface)
+ * @see #setPlayer(MediaPlayerBase)
* @see Builder#setVolumeProvider(VolumeProvider2)
*/
- public void setPlayer(@NonNull MediaPlayerInterface player,
+ public void setPlayer(@NonNull MediaPlayerBase player,
@NonNull VolumeProvider2 volumeProvider) {
mProvider.setPlayer_impl(player, volumeProvider);
}
@@ -1093,7 +1158,7 @@
* @return player
*/
public @Nullable
- MediaPlayerInterface getPlayer() {
+ MediaPlayerBase getPlayer() {
return mProvider.getPlayer_impl();
}
@@ -1123,7 +1188,9 @@
* @param focusGain the type of audio focus gain that will be requested, or
* {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during
* playback.
+ * @hide
*/
+ // TODO(jaewan): Revisit
public void setAudioFocusRequest(int focusGain) {
mProvider.setAudioFocusRequest_impl(focusGain);
}
@@ -1164,14 +1231,6 @@
}
/**
- * Notify changes in metadata of previously set playlist. Controller will get the whole set of
- * playlist again.
- */
- public void notifyMetadataChanged() {
- mProvider.notifyMetadataChanged_impl();
- }
-
- /**
* Send custom command to all connected controllers.
*
* @param command a command
@@ -1264,14 +1323,14 @@
}
/**
- * Sets the index of current DataSourceDesc in the play list to be played.
+ * Skip to the item in the play list.
*
- * @param index the index of DataSourceDesc in the play list you want to play
+ * @param item item in the play list you want to play
* @throws IllegalArgumentException if the play list is null
* @throws NullPointerException if index is outside play list range
*/
- public void setCurrentPlaylistItem(int index) {
- mProvider.setCurrentPlaylistItem_impl(index);
+ public void skipToPlaylistItem(MediaItem2 item) {
+ mProvider.skipToPlaylistItem_impl(item);
}
/**
@@ -1289,7 +1348,7 @@
}
/**
- * Sets a list of {@link MediaItem2} as the current play list.
+ * Set a list of {@link MediaItem2} as the current play list.
*
* @param playlist A list of {@link MediaItem2} objects to set as a play list.
* @throws IllegalArgumentException if given {@param playlist} is null.
@@ -1299,13 +1358,68 @@
}
/**
- * Returns the playlist which is lastly set.
+ * Remove the media item at index in the play list.
+ * <p>
+ * If index is same as the current index of the playlist, current playback
+ * will be stopped and playback moves to next source in the list.
+ *
+ * @throws IllegalArgumentException if the play list is null
*/
+ // TODO(jaewan): Remove with index was previously rejected by council (b/36524925)
+ // TODO(jaewan): Should we also add movePlaylistItem from index to index?
+ public void removePlaylistItem(MediaItem2 item) {
+ mProvider.removePlaylistItem_impl(item);
+ }
+
+ /**
+ * Add the media item to the play list at position index.
+ * <p>
+ * This will not change the currently playing media item.
+ * If index is less than or equal to the current index of the play list,
+ * the current index of the play list will be incremented correspondingly.
+ *
+ * @param index the index you want to add
+ * @param item the media item you want to add
+ * @throws IndexOutOfBoundsException if index is outside play list range
+ */
+ @Override
+ public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+ mProvider.addPlaylistItem_impl(index, item);
+ }
+
+ /**
+ * Edit the media item to the play list at position index. This is expected to be called when
+ * the metadata information is updated.
+ * <p>
+ * This will not change the currently playing media item.
+ *
+ * @param item the media item you want to add to the play list
+ */
+ public void editPlaylistItem(@NonNull MediaItem2 item) {
+ mProvider.editPlaylistItem_impl(item);
+ }
+
+ /**
+ * Return the playlist which is lastly set.
+ *
+ * @return playlist
+ */
+ @Override
public List<MediaItem2> getPlaylist() {
return mProvider.getPlaylist_impl();
}
/**
+ * Return currently playing media item.
+ *
+ * @return currently playing media item
+ */
+ @Override
+ public MediaItem2 getCurrentPlaylistItem() {
+ return mProvider.getCurrentPlaylistItem_impl();
+ }
+
+ /**
* Sets the {@link PlaylistParams} for the current play list. Repeat/shuffle mode and metadata
* for the list can be set by calling this method.
*
@@ -1324,16 +1438,28 @@
return mProvider.getPlaylistParams_impl();
}
- /*
+ /**
+ * Notify errors to the connected controllers
+ *
+ * @param errorCode error code
+ * @param extra extra
+ */
+ public void notifyError(@ErrorCode int errorCode, int extra) {
+ mProvider.notifyError_impl(errorCode, extra);
+ }
+
+ /**
* Register {@link EventCallback} to listen changes in the underlying
- * {@link MediaPlayerInterface}, regardless of the change in the underlying player.
+ * {@link MediaPlayerBase}, regardless of the change in the underlying player.
* <p>
* Registered callbacks will be also called when the underlying player is changed.
*
* @param executor a callback Executor
* @param callback a EventCallback
* @throws IllegalArgumentException if executor or callback is {@code null}.
+ * @hide
*/
+ // TODO(jaewan): Unhide or remove
public void registerPlayerEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull EventCallback callback) {
mProvider.registerPlayerEventCallback_impl(executor, callback);
@@ -1344,7 +1470,9 @@
*
* @param callback the callback to be removed
* @throws IllegalArgumentException if the callback is {@code null}.
+ * @hide
*/
+ // TODO(jaewan): Unhide or remove
public void unregisterPlayerEventCallback(@NonNull EventCallback callback) {
mProvider.unregisterPlayerEventCallback_impl(callback);
}
@@ -1353,6 +1481,7 @@
* Return the {@link PlaybackState2} from the player.
*
* @return playback state
+ * @hide
*/
public PlaybackState2 getPlaybackState() {
return mProvider.getPlaybackState_impl();
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 0b5dddf..56e8e5d 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -90,7 +90,7 @@
* rejected, the controller will unbind. If it's accepted, the controller will be available to use
* and keep binding.
* <p>
- * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState2)}
+ * When playback is started for this session service, {@link #onUpdateNotification()}
* is called and service would become a foreground service. It's needed to keep playback after the
* controller is destroyed. The session service becomes background service when the playback is
* stopped.
@@ -100,7 +100,6 @@
* Any app can bind to the session service with controller, but the controller can be used only if
* the session service accepted the connection request through
* {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
- * @hide
*/
public abstract class MediaSessionService2 extends Service {
private final MediaSessionService2Provider mProvider;
@@ -158,17 +157,16 @@
public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
/**
- * Called when the playback state of this session is changed, and notification needs update.
- * Override this method to show your own notification UI.
+ * Called when the playback state of this session is changed so notification needs update.
+ * Override this method to show or cancel your own notification UI.
* <p>
* With the notification returned here, the service become foreground service when the playback
* is started. It becomes background service after the playback is stopped.
*
- * @param state playback state
* @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
*/
- public MediaNotification onUpdateNotification(PlaybackState2 state) {
- return mProvider.onUpdateNotification_impl(state);
+ public MediaNotification onUpdateNotification() {
+ return mProvider.onUpdateNotification_impl();
}
/**
@@ -201,9 +199,9 @@
}
/**
- * Returned by {@link #onUpdateNotification(PlaybackState2)} for making session service
- * foreground service to keep playback running in the background. It's highly recommended to
- * show media style notification here.
+ * Returned by {@link #onUpdateNotification()} for making session service forground service
+ * to keep playback running in the background. It's highly recommended to show media style
+ * notification here.
*/
public static class MediaNotification {
private final MediaNotificationProvider mProvider;
diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java
index a95b8f2..7afb579 100644
--- a/media/java/android/media/PlaybackState2.java
+++ b/media/java/android/media/PlaybackState2.java
@@ -28,11 +28,12 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Playback state for a {@link MediaPlayerInterface}, to be shared between {@link MediaSession2} and
+ * Playback state for a {@link MediaPlayerBase}, to be shared between {@link MediaSession2} and
* {@link MediaController2}. This includes a playback state {@link #STATE_PLAYING},
* the current playback position and extra.
* @hide
*/
+// TODO(jaewan): Remove this.
public final class PlaybackState2 {
// Similar to the PlaybackState with following changes
// - Not implement Parcelable and added from/toBundle()
@@ -214,4 +215,4 @@
@Nullable Bundle bundle) {
return ApiLoader.getProvider(context).fromBundle_PlaybackState2(context, bundle);
}
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 4f77ecd..e5b05fb 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.Rating2Provider;
@@ -36,12 +35,10 @@
* {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
* be defined as "unrated"), both of which are defined when the rating instance is constructed
* through one of the factory methods.
- * @hide
*/
+// New version of Rating with following change
+// - Don't implement Parcelable for updatable support.
public final class Rating2 {
- // Mostly same as the android.media.Rating, but it's no longer implements Parcelable for
- // updatable support.
-
/**
* @hide
*/
@@ -100,7 +97,6 @@
/**
* @hide
*/
- @SystemApi
public Rating2(@NonNull Rating2Provider provider) {
mProvider = provider;
}
@@ -113,7 +109,6 @@
/**
* @hide
*/
- @SystemApi
public Rating2Provider getProvider() {
return mProvider;
}
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index 2c2090c..fdfa43a 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaSessionManager;
import android.media.update.ApiLoader;
@@ -36,8 +35,11 @@
* {@link MediaController2} to communicate with the session.
* <p>
* It can be also obtained by {@link MediaSessionManager}.
- * @hide
*/
+// New version of MediaSession.Token for following reasons
+// - Stop implementing Parcelable for updatable support
+// - Represent session and library service (formerly browser service) in one class.
+// Previously MediaSession.Token was for session and ComponentName was for service.
public final class SessionToken2 {
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
@@ -86,7 +88,6 @@
* Constructor for the token.
* @hide
*/
- @SystemApi
public SessionToken2(@NonNull SessionToken2Provider provider) {
mProvider = provider;
}
@@ -106,7 +107,9 @@
return mProvider.toString_impl();
}
- @SystemApi
+ /**
+ * @hide
+ */
public SessionToken2Provider getProvider() {
return mProvider;
}
@@ -147,7 +150,7 @@
* @return
*/
public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) {
- return ApiLoader.getProvider(context).SessionToken2_fromBundle(context, bundle);
+ return ApiLoader.getProvider(context).fromBundle_SessionToken2(context, bundle);
}
/**
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 53ba466..8e1cfbf 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.VolumeProvider2Provider;
@@ -32,12 +31,11 @@
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
* <p>
* You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}.
- *
- * @hide
+ * {@link MediaSession2#setPlayer(MediaPlayerBase, VolumeProvider2)}.
*/
+// New version of VolumeProvider with following changes
+// - Don't implement Parcelable for updatable support.
public abstract class VolumeProvider2 {
-
/**
* @hide
*/
@@ -85,7 +83,6 @@
/**
* @hide
*/
- @SystemApi
public VolumeProvider2Provider getProvider() {
return mProvider;
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index e7fcc7f..d079b7a 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -343,7 +343,6 @@
* Called when a {@link MediaSession2} is created.
* @hide
*/
- // TODO(jaewan): System API
public boolean createSession2(@NonNull SessionToken2 token) {
if (token == null) {
return false;
@@ -356,10 +355,10 @@
return false;
}
- /** Called when a {@link MediaSession2} is destroyed.
+ /**
+ * Called when a {@link MediaSession2} is destroyed.
* @hide
*/
- // TODO(jaewan): System API
public void destroySession2(@NonNull SessionToken2 token) {
if (token == null) {
return;
@@ -381,9 +380,7 @@
* using the {@link NotificationListenerService} APIs.
*
* @return list of tokens
- * @hide
*/
- // TODO(jaewan): Unhide
public List<SessionToken2> getActiveSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
@@ -404,9 +401,7 @@
* using the {@link NotificationListenerService} APIs.
*
* @return list of tokens
- * @hide
*/
- // TODO(jaewan): Unhide
public List<SessionToken2> getSessionServiceTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
@@ -429,9 +424,7 @@
* @return list of tokens
* @see #getActiveSessionTokens
* @see #getSessionServiceTokens
- * @hide
*/
- // TODO(jaewan): Unhide
public List<SessionToken2> getAllSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
@@ -452,9 +445,7 @@
*
* @param executor executor to run this command
* @param listener The listener to add.
- * @hide
*/
- // TODO(jaewan): Unhide
public void addOnSessionTokensChangedListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnSessionTokensChangedListener listener) {
addOnSessionTokensChangedListener(UserHandle.myUserId(), executor, listener);
@@ -501,9 +492,7 @@
* Stop receiving session token updates on the specified listener.
*
* @param listener The listener to remove.
- * @hide
*/
- // TODO(jaewan): Unhide
public void removeOnSessionTokensChangedListener(
@NonNull OnSessionTokensChangedListener listener) {
if (listener == null) {
@@ -660,9 +649,7 @@
/**
* Listens for changes to the {@link #getAllSessionTokens()}. This can be added
* using {@link #addOnActiveSessionsChangedListener}.
- * @hide
*/
- // TODO(jaewan): Unhide
public interface OnSessionTokensChangedListener {
void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 8d9efd5..ca5c16d 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -16,7 +16,6 @@
package android.media.update;
-import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.media.AudioAttributes;
import android.media.MediaController2.PlaybackInfo;
@@ -65,6 +64,11 @@
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaybackState2 getPlaybackState_impl();
+ int getPlayerState_impl();
+ long getPosition_impl();
+ float getPlaybackSpeed_impl();
+ long getBufferedPosition_impl();
+ MediaItem2 getCurrentPlaylistItem_impl();
interface PlaybackInfoProvider {
int getPlaybackType_impl();
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
index 2970f0e..1d5b414 100644
--- a/media/java/android/media/update/MediaItem2Provider.java
+++ b/media/java/android/media/update/MediaItem2Provider.java
@@ -23,7 +23,6 @@
/**
* @hide
*/
-// TODO(jaewan): SystemApi
public interface MediaItem2Provider {
Bundle toBundle_impl();
String toString_impl();
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index 7489f76..9a0d693 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -16,16 +16,12 @@
package android.media.update;
-import android.annotation.SystemApi;
-import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaSession2.ControllerInfo;
import android.os.Bundle;
/**
* @hide
*/
-// TODO: @SystemApi
public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
// Nothing new for now
diff --git a/media/java/android/media/update/MediaMetadata2Provider.java b/media/java/android/media/update/MediaMetadata2Provider.java
index b6e5c8a..22463e9 100644
--- a/media/java/android/media/update/MediaMetadata2Provider.java
+++ b/media/java/android/media/update/MediaMetadata2Provider.java
@@ -11,7 +11,6 @@
/**
* @hide
*/
-// TODO(jaewan): SystemApi
public interface MediaMetadata2Provider {
boolean containsKey_impl(String key);
CharSequence getText_impl(String key);
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index fc1f671..dbd4a0a 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -19,8 +19,8 @@
import android.app.PendingIntent;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.EventCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -40,27 +40,28 @@
/**
* @hide
*/
-// TODO: @SystemApi
public interface MediaSession2Provider extends TransportControlProvider {
void close_impl();
- void setPlayer_impl(MediaPlayerInterface player);
- void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider);
- MediaPlayerInterface getPlayer_impl();
+ void setPlayer_impl(MediaPlayerBase player);
+ void setPlayer_impl(MediaPlayerBase player, VolumeProvider2 volumeProvider);
+ MediaPlayerBase getPlayer_impl();
SessionToken2 getToken_impl();
List<ControllerInfo> getConnectedControllers_impl();
void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
void setAudioFocusRequest_impl(int focusGain);
-
void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
- void notifyMetadataChanged_impl();
void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver);
void sendCustomCommand_impl(Command command, Bundle args);
void setPlaylist_impl(List<MediaItem2> playlist);
+ void addPlaylistItem_impl(int index, MediaItem2 item);
+ void removePlaylistItem_impl(MediaItem2 item);
+ void editPlaylistItem_impl(MediaItem2 item);
List<MediaItem2> getPlaylist_impl();
+ MediaItem2 getCurrentPlaylistItem_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaylistParams getPlaylistParams_impl();
-
+ void notifyError_impl(int errorCode, int extra);
void registerPlayerEventCallback_impl(Executor executor, EventCallback callback);
void unregisterPlayerEventCallback_impl(EventCallback callback);
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 42e7587..8697e70 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -16,7 +16,6 @@
package android.media.update;
-import android.annotation.SystemApi;
import android.app.Notification;
import android.content.Intent;
import android.media.MediaSession2;
@@ -29,7 +28,7 @@
*/
public interface MediaSessionService2Provider {
MediaSession2 getSession_impl();
- MediaNotification onUpdateNotification_impl(PlaybackState2 state);
+ MediaNotification onUpdateNotification_impl();
// Service
void onCreate_impl();
diff --git a/media/java/android/media/update/PlaybackState2Provider.java b/media/java/android/media/update/PlaybackState2Provider.java
index 93f769c..66b8fa5 100644
--- a/media/java/android/media/update/PlaybackState2Provider.java
+++ b/media/java/android/media/update/PlaybackState2Provider.java
@@ -21,7 +21,6 @@
/**
* @hide
*/
-// TODO(jaewan): @SystemApi
public interface PlaybackState2Provider {
String toString_impl();
diff --git a/media/java/android/media/update/Rating2Provider.java b/media/java/android/media/update/Rating2Provider.java
index 8966196..28ad273 100644
--- a/media/java/android/media/update/Rating2Provider.java
+++ b/media/java/android/media/update/Rating2Provider.java
@@ -22,7 +22,6 @@
/**
* @hide
*/
-// TODO(jaewan): @SystemApi
public interface Rating2Provider {
String toString_impl();
boolean equals_impl(Object obj);
@@ -34,4 +33,4 @@
boolean isThumbUp_impl();
float getStarRating_impl();
float getPercentRating_impl();
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 29a30343..62759eb 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -28,10 +28,9 @@
import android.media.MediaLibraryService2;
import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
import android.media.MediaSession2;
import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.PlaylistParams;
@@ -44,7 +43,7 @@
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
import android.media.update.MediaSession2Provider.CommandGroupProvider;
import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
@@ -86,9 +85,10 @@
PlaylistParams playlistParams, int repeatMode, int shuffleMode,
MediaMetadata2 playlistMetadata);
PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
- BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
+ CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+ MediaSession2.CommandButton.Builder builder);
BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
- Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
+ Context context, MediaSession2.Builder instance, MediaPlayerBase player);
MediaController2Provider createMediaController2(Context context, MediaController2 instance,
SessionToken2 token, Executor executor, ControllerCallback callback);
@@ -103,14 +103,15 @@
MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
createMediaLibraryService2Builder(
- Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
- Executor callbackExecutor, MediaLibrarySessionCallback callback);
+ MediaLibraryService2 service, MediaLibrarySession.Builder instance,
+ MediaPlayerBase player, Executor callbackExecutor,
+ MediaLibrarySessionCallback callback);
LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
String rootId, Bundle extras);
SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
String packageName, String serviceName, int uid);
- SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle);
+ SessionToken2 fromBundle_SessionToken2(Context context, Bundle bundle);
MediaItem2Provider createMediaItem2(Context context, MediaItem2 mediaItem2,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags);
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index 44f82b29..9af8ada 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -16,6 +16,7 @@
package android.media.update;
+import android.media.MediaItem2;
import android.media.PlaybackState2;
/**
@@ -32,7 +33,7 @@
void fastForward_impl();
void rewind_impl();
void seekTo_impl(long pos);
- void setCurrentPlaylistItem_impl(int index);
+ void skipToPlaylistItem_impl(MediaItem2 item);
PlaybackState2 getPlaybackState_impl();
}
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 4333c96..152ace9 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -19,7 +19,7 @@
import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
@@ -65,7 +65,7 @@
/**
* @hide
*/
- void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player);
+ void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
// TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
void setVideoPath_impl(String path);
diff --git a/media/java/android/media/update/VolumeProvider2Provider.java b/media/java/android/media/update/VolumeProvider2Provider.java
index 5657af6..5b5cfd3 100644
--- a/media/java/android/media/update/VolumeProvider2Provider.java
+++ b/media/java/android/media/update/VolumeProvider2Provider.java
@@ -18,7 +18,6 @@
/**
* @hide
*/
-// TODO(jaewan): @SystemApi
public interface VolumeProvider2Provider {
int getControlType_impl();
int getMaxVolume_impl();
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index c78f454..58d5db3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -553,7 +553,7 @@
<string name="select_private_dns_configuration_title">Private DNS</string>
<string name="select_private_dns_configuration_dialog_title">Select Private DNS Mode</string>
<string name="private_dns_mode_off">Off</string>
- <string name="private_dns_mode_opportunistic">Opportunistic</string>
+ <string name="private_dns_mode_opportunistic">Automatic</string>
<string name="private_dns_mode_provider">Private DNS provider hostname</string>
<string name="private_dns_mode_provider_hostname_hint">Enter hostname of DNS provider</string>
diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml
deleted file mode 100644
index 43197c4..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<com.android.systemui.qs.QSTooltipView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_header_tooltip_height"
- android:alpha="0"
- android:gravity="center_horizontal|bottom"
- android:visibility="invisible">
-
- <TextView
- android:id="@+id/header_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/quick_settings_header_onboarding_text"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"
- android:textColor="?android:attr/colorAccent" />
-
-</com.android.systemui.qs.QSTooltipView>
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
new file mode 100644
index 0000000..89d6e99
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/header_text_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_header_tooltip_height"
+ android:layout_below="@id/quick_status_bar_system_icons"
+ android:layout_marginTop="12dp">
+
+ <TextView
+ android:id="@+id/long_press_tooltip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:alpha="0"
+ android:text="@string/quick_settings_header_onboarding_text"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textColor="?android:attr/colorAccent"
+ android:visibility="invisible" />
+
+ <LinearLayout
+ android:id="@+id/next_alarm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:gravity="center_vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:layout_width="@dimen/qs_header_alarm_icon_size"
+ android:layout_height="@dimen/qs_header_alarm_icon_size"
+ android:src="@drawable/stat_sys_alarm"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/next_alarm_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel" />
+
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index cc79d0d..959247e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,8 +32,13 @@
android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
+
+ <!-- Status icons within the panel itself (and not in the top-most status bar) -->
<include layout="@layout/quick_qs_status_icons" />
+ <!-- Layout containing tooltips, alarm text, etc. -->
+ <include layout="@layout/quick_settings_header_info" />
+
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aefcb55..dc230d4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -321,7 +321,7 @@
<dimen name="qs_tile_padding_bottom">16dp</dimen>
<dimen name="qs_tile_spacing">4dp</dimen>
<dimen name="qs_panel_padding_bottom">0dp</dimen>
- <dimen name="qs_panel_padding_top">32dp</dimen>
+ <dimen name="qs_panel_padding_top">30dp</dimen>
<dimen name="qs_detail_header_height">56dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>
@@ -345,7 +345,9 @@
<dimen name="qs_detail_item_icon_width">32dp</dimen>
<dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
<dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
- <dimen name="qs_header_tooltip_height">30dp</dimen>
+ <dimen name="qs_header_tooltip_height">18dp</dimen>
+ <dimen name="qs_header_alarm_icon_size">18dp</dimen>
+ <dimen name="qs_header_alarm_text_margin_start">6dp</dimen>
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">24dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f3f8d91f..6098e4e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -736,7 +736,8 @@
private DisplayClientState mDisplayClientState = new DisplayClientState();
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @VisibleForTesting
+ final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -773,6 +774,13 @@
maxChargingMicroWatt));
mHandler.sendMessage(msg);
} else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ // ACTION_SIM_STATE_CHANGED is rebroadcast after unlocking the device to
+ // keep compatibility with apps that aren't direct boot aware.
+ // SysUI should just ignore this broadcast because it was already received
+ // and processed previously.
+ if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ return;
+ }
SimData args = SimData.fromIntent(intent);
if (DEBUG_SIM_STATES) {
Log.v(TAG, "action " + action
@@ -1508,7 +1516,8 @@
/**
* Handle {@link #MSG_SIM_STATE_CHANGE}
*/
- private void handleSimStateChange(int subId, int slotId, State state) {
+ @VisibleForTesting
+ protected void handleSimStateChange(int subId, int slotId, State state) {
if (DEBUG_SIM_STATES) {
Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 175cddc..2a4bb60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -66,10 +66,6 @@
private TouchAnimator mNonfirstPageDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
- /**
- * Whether we're in the middle of animating between the collapsed and expanded states.
- */
- private boolean mIsAnimating;
private boolean mOnKeyguard;
private boolean mAllowFancy;
@@ -94,9 +90,6 @@
Log.w(TAG, "QS Not using page layout");
}
panel.setPageListener(this);
-
- // At time of creation, the QS panel is never animating.
- mIsAnimating = false;
}
public void onRtlChanged() {
@@ -251,11 +244,6 @@
} else {
mBrightnessAnimator = null;
}
- View headerView = mQsPanel.getHeaderView();
- if (headerView!= null) {
- firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0);
- mAllViews.add(headerView);
- }
mFirstPageAnimator = firstPageBuilder
.setListener(this)
.build();
@@ -342,21 +330,11 @@
@Override
public void onAnimationAtStart() {
- if (mIsAnimating) {
- mQsPanel.onCollapse();
- }
- mIsAnimating = false;
-
mQuickQsPanel.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationAtEnd() {
- if (mIsAnimating) {
- mQsPanel.onExpanded();
- }
- mIsAnimating = false;
-
mQuickQsPanel.setVisibility(View.INVISIBLE);
final int N = mQuickQsViews.size();
for (int i = 0; i < N; i++) {
@@ -366,11 +344,6 @@
@Override
public void onAnimationStarted() {
- if (!mIsAnimating) {
- mQsPanel.onAnimating();
- }
- mIsAnimating = true;
-
mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
if (mOnFirstPage) {
final int N = mQuickQsViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d437f49..5758762 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -272,21 +272,27 @@
mContainer.setExpansion(expansion);
final float translationScaleY = expansion - 1;
if (!mHeaderAnimating) {
- int height = mHeader.getHeight();
- getView().setTranslationY(mKeyguardShowing ? (translationScaleY * height)
- : headerTranslation);
+ getView().setTranslationY(
+ mKeyguardShowing
+ ? translationScaleY * mHeader.getHeight()
+ : headerTranslation);
}
if (expansion == mLastQSExpansion) {
return;
}
mLastQSExpansion = expansion;
- mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
- mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
+
+ boolean fullyExpanded = expansion == 1;
int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom()
+ mFooter.getHeight();
+ float panelTranslationY = translationScaleY * heightDiff;
+
+ // Let the views animate their contents correctly by giving them the necessary context.
+ mHeader.setExpansion(mKeyguardShowing, expansion, panelTranslationY);
+ mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
mQSPanel.setTranslationY(translationScaleY * heightDiff);
- boolean fullyExpanded = expansion == 1;
mQSDetail.setFullyExpanded(fullyExpanded);
+
if (fullyExpanded) {
// Always draw within the bounds of the view when fully expanded.
mQSPanel.setClipBounds(null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index a92e346..143ad21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -29,7 +29,6 @@
import android.service.quicksettings.Tile;
import android.util.AttributeSet;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
@@ -54,11 +53,11 @@
import java.util.ArrayList;
import java.util.Collection;
-/** View that represents the quick settings tile panel. **/
+/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
- public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press";
+ public static final String QS_SHOW_HEADER = "qs_show_header";
protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -74,7 +73,6 @@
private BrightnessController mBrightnessController;
protected QSTileHost mHost;
- protected QSTooltipView mTooltipView;
protected QSSecurityFooter mFooter;
private boolean mGridContentVisible = true;
@@ -96,11 +94,6 @@
setOrientation(VERTICAL);
- mTooltipView = (QSTooltipView) LayoutInflater.from(mContext)
- .inflate(R.layout.quick_settings_header, this, false);
-
- addView(mTooltipView);
-
mBrightnessView = LayoutInflater.from(mContext).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
addView(mBrightnessView);
@@ -152,7 +145,6 @@
super.onAttachedToWindow();
final TunerService tunerService = Dependency.get(TunerService.class);
tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
- tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP);
if (mHost != null) {
setTiles(mHost.getTiles());
@@ -186,8 +178,6 @@
public void onTuningChanged(String key, String newValue) {
if (QS_SHOW_BRIGHTNESS.equals(key)) {
updateViewVisibilityForTuningValue(mBrightnessView, newValue);
- } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
- updateViewVisibilityForTuningValue(mTooltipView, newValue);
}
}
@@ -229,10 +219,6 @@
return mBrightnessView;
}
- View getHeaderView() {
- return mTooltipView;
- }
-
public void setCallback(QSDetail.Callback callback) {
mCallback = callback;
}
@@ -254,10 +240,7 @@
public void updateResources() {
final Resources res = mContext.getResources();
- setPadding(0, 0, 0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
- mTooltipView.getLayoutParams().height =
- res.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
- mTooltipView.setLayoutParams(mTooltipView.getLayoutParams());
+ setPadding(0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_top), 0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
for (TileRecord r : mRecords) {
r.tile.clearState();
}
@@ -291,21 +274,6 @@
if (mCustomizePanel != null && mCustomizePanel.isShown()) {
mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
}
-
- // Instantly hide the header here since we don't want it to still be animating.
- mTooltipView.setVisibility(View.INVISIBLE);
- }
-
- /**
- * Called when the panel is fully animated out/expanded. This is different from the state
- * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out.
- */
- public void onExpanded() {
- mTooltipView.fadeIn();
- }
-
- public void onAnimating() {
- mTooltipView.fadeOut();
}
public void setExpanded(boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
deleted file mode 100644
index d1f9741..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.qs;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Prefs;
-
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * Tooltip/header view for the Quick Settings panel.
- */
-public class QSTooltipView extends LinearLayout {
-
- private static final int FADE_ANIMATION_DURATION_MS = 300;
- private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6);
- private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
- public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
-
- private final Handler mHandler = new Handler();
- private final Runnable mAutoFadeOutRunnable = () -> fadeOut();
-
- private int mShownCount;
-
- public QSTooltipView(Context context) {
- this(context, null);
- }
-
- public QSTooltipView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mShownCount = getStoredShownCount();
- }
-
- /** Returns the latest stored tooltip shown count from SharedPreferences. */
- private int getStoredShownCount() {
- return Prefs.getInt(
- mContext,
- Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- TOOLTIP_NOT_YET_SHOWN_COUNT);
- }
-
- /**
- * Fades in the header view if we can show the tooltip - short circuits any running animation.
- */
- public void fadeIn() {
- if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
- animate().cancel();
- setVisibility(View.VISIBLE);
- animate()
- .alpha(1f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS);
- }
- })
- .start();
-
- // Increment and drop the shown count in prefs for the next time we're deciding to
- // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
- // in prefs (say, from a long press).
- if (getStoredShownCount() <= mShownCount) {
- Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
- }
- }
- }
-
- /**
- * Fades out the header view if it's partially visible - short circuits any running animation.
- */
- public void fadeOut() {
- animate().cancel();
- if (getVisibility() == View.VISIBLE && getAlpha() != 0f) {
- mHandler.removeCallbacks(mAutoFadeOutRunnable);
- animate()
- .alpha(0f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- perhapsMakeViewInvisible();
- }
- })
- .start();
- } else {
- perhapsMakeViewInvisible();
- }
- }
-
- /**
- * Only update visibility if the view is currently being shown. Otherwise, it's already been
- * hidden by some other manner.
- */
- private void perhapsMakeViewInvisible() {
- if (getVisibility() == View.VISIBLE) {
- setVisibility(View.INVISIBLE);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f0684e1..2270b60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -20,7 +20,6 @@
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Space;
@@ -123,7 +122,7 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+ if (QS_SHOW_BRIGHTNESS.equals(key)) {
// No Brightness or Tooltip for you!
super.onTuningChanged(key, "0");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 4d7333b..78481d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -15,24 +15,30 @@
package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static android.app.StatusBarManager.DISABLE_NONE;
+import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.AlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
+import android.os.Handler;
import android.provider.AlarmClock;
import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;
-import android.widget.TextClock;
+import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.R.id;
import com.android.systemui.SysUiServiceProvider;
@@ -43,11 +49,23 @@
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.policy.NextAlarmController;
-public class QuickStatusBarHeader extends RelativeLayout
- implements CommandQueue.Callbacks, View.OnClickListener {
+/**
+ * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
+ * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
+ * contents.
+ */
+public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks,
+ View.OnClickListener, NextAlarmController.NextAlarmChangeCallback {
- private ActivityStarter mActivityStarter;
+ /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
+ private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
+ private static final int FADE_ANIMATION_DURATION_MS = 300;
+ private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
+ public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
+
+ private final Handler mHandler = new Handler();
private QSPanel mQsPanel;
@@ -58,20 +76,39 @@
protected QuickQSPanel mHeaderQsPanel;
protected QSTileHost mHost;
private TintedIconManager mIconManager;
- private TouchAnimator mAlphaAnimator;
+ private TouchAnimator mStatusIconsAlphaAnimator;
+ private TouchAnimator mHeaderTextContainerAlphaAnimator;
private View mQuickQsStatusIcons;
-
private View mDate;
+ private View mHeaderTextContainerView;
+ /** View corresponding to the next alarm info (including the icon). */
+ private View mNextAlarmView;
+ /** Tooltip for educating users that they can long press on icons to see more details. */
+ private View mLongPressTooltipView;
+ /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
+ private TextView mNextAlarmTextView;
+
+ private NextAlarmController mAlarmController;
+ private String mNextAlarmText;
+ /** Counts how many times the long press tooltip has been shown to the user. */
+ private int mShownCount;
+
+ /**
+ * Runnable for automatically fading out the long press tooltip (as if it were animating away).
+ */
+ private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mAlarmController = Dependency.get(NextAlarmController.class);
+ mShownCount = getStoredShownCount();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- Resources res = getResources();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
mDate = findViewById(R.id.date);
@@ -79,8 +116,11 @@
mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
- // RenderThread is doing more harm than good when touching the header (to expand quick
- // settings), so disable it for this view
+ // Views corresponding to the header info section (e.g. tooltip and next alarm).
+ mHeaderTextContainerView = findViewById(R.id.header_text_container);
+ mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
+ mNextAlarmView = findViewById(R.id.next_alarm);
+ mNextAlarmTextView = findViewById(R.id.next_alarm_text);
updateResources();
@@ -98,8 +138,6 @@
BatteryMeterView battery = findViewById(R.id.battery);
battery.setForceShowPercent(true);
-
- mActivityStarter = Dependency.get(ActivityStarter.class);
}
private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
@@ -129,21 +167,26 @@
}
private void updateResources() {
- updateAlphaAnimator();
+ // Update height, especially due to landscape mode restricting space.
+ mHeaderTextContainerView.getLayoutParams().height =
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
+ mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
+
+ updateStatusIconAlphaAnimator();
+ updateHeaderTextContainerAlphaAnimator();
}
- private void updateAlphaAnimator() {
- mAlphaAnimator = new TouchAnimator.Builder()
+ private void updateStatusIconAlphaAnimator() {
+ mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsStatusIcons, "alpha", 1, 0)
.build();
}
- public int getCollapsedHeight() {
- return getHeight();
- }
-
- public int getExpandedHeight() {
- return getHeight();
+ private void updateHeaderTextContainerAlphaAnimator() {
+ mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
+ .addFloat(mHeaderTextContainerView, "alpha", 0, 1)
+ .setStartDelay(.5f)
+ .build();
}
public void setExpanded(boolean expanded) {
@@ -153,10 +196,47 @@
updateEverything();
}
- public void setExpansion(float headerExpansionFraction) {
- if (mAlphaAnimator != null ) {
- mAlphaAnimator.setPosition(headerExpansionFraction);
+ /**
+ * Animates the inner contents based on the given expansion details.
+ *
+ * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen)
+ * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
+ * @param panelTranslationY how much the panel has physically moved down vertically (required
+ * for keyguard animations only)
+ */
+ public void setExpansion(boolean isKeyguardShowing, float expansionFraction,
+ float panelTranslationY) {
+ final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction;
+ if (mStatusIconsAlphaAnimator != null) {
+ mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
}
+
+ if (isKeyguardShowing) {
+ // If the keyguard is showing, we want to offset the text so that it comes in at the
+ // same time as the panel as it slides down.
+ mHeaderTextContainerView.setTranslationY(panelTranslationY);
+ } else {
+ mHeaderTextContainerView.setTranslationY(0f);
+ }
+
+ if (mHeaderTextContainerAlphaAnimator != null) {
+ mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
+
+ // Check the original expansion fraction - we don't want to show the tooltip until the
+ // panel is pulled all the way out.
+ if (expansionFraction == 1f) {
+ // QS is fully expanded, bring in the tooltip.
+ showLongPressTooltip();
+ }
+ }
+
+ /** Returns the latest stored tooltip shown count from SharedPreferences. */
+ private int getStoredShownCount() {
+ return Prefs.getInt(
+ mContext,
+ Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+ TOOLTIP_NOT_YET_SHOWN_COUNT);
}
@Override
@@ -191,6 +271,12 @@
}
mHeaderQsPanel.setListening(listening);
mListening = listening;
+
+ if (listening) {
+ mAlarmController.addCallback(this);
+ } else {
+ mAlarmController.removeCallback(this);
+ }
}
@Override
@@ -201,6 +287,125 @@
}
}
+ @Override
+ public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
+ mNextAlarmText = nextAlarm != null ? formatNextAlarm(mContext, nextAlarm) : null;
+ if (mNextAlarmText != null) {
+ hideLongPressTooltip(true /* shouldFadeInAlarmText */);
+ } else {
+ hideAlarmText();
+ }
+ updateHeaderTextContainerAlphaAnimator();
+ }
+
+ /**
+ * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
+ * the space).
+ */
+ public void showLongPressTooltip() {
+ // If we have alarm text to show, don't bother fading in the tooltip.
+ if (!TextUtils.isEmpty(mNextAlarmText)) {
+ return;
+ }
+
+ if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
+ mLongPressTooltipView.animate().cancel();
+ mLongPressTooltipView.setVisibility(View.VISIBLE);
+ mLongPressTooltipView.animate()
+ .alpha(1f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHandler.postDelayed(
+ mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
+ }
+ })
+ .start();
+
+ // Increment and drop the shown count in prefs for the next time we're deciding to
+ // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
+ // in prefs (say, from a long press).
+ if (getStoredShownCount() <= mShownCount) {
+ Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
+ }
+ }
+ }
+
+ /**
+ * Fades out the long press tooltip if it's partially visible - short circuits any running
+ * animation. Additionally has the ability to fade in the alarm info text.
+ *
+ * @param shouldShowAlarmText whether we should fade in the next alarm text
+ */
+ private void hideLongPressTooltip(boolean shouldShowAlarmText) {
+ mLongPressTooltipView.animate().cancel();
+ if (mLongPressTooltipView.getVisibility() == View.VISIBLE
+ && mLongPressTooltipView.getAlpha() != 0f) {
+ mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
+ mLongPressTooltipView.animate()
+ .alpha(0f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLongPressTooltipView.setVisibility(View.INVISIBLE);
+
+ if (shouldShowAlarmText) {
+ showAlarmText();
+ }
+ }
+ })
+ .start();
+ } else {
+ mLongPressTooltipView.setVisibility(View.INVISIBLE);
+
+ if (shouldShowAlarmText) {
+ showAlarmText();
+ }
+ }
+ }
+
+ /**
+ * Fades in the updated alarm text. Note that if there's already an alarm showing, this will
+ * immediately hide it and fade in the updated time.
+ */
+ private void showAlarmText() {
+ mNextAlarmView.setAlpha(0f);
+ mNextAlarmView.setVisibility(View.VISIBLE);
+ mNextAlarmTextView.setText(mNextAlarmText);
+
+ mNextAlarmView.animate()
+ .alpha(1f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .start();
+ }
+
+ /**
+ * Fades out and hides the next alarm text. This also resets the text contents to null in
+ * preparation for the next alarm update.
+ */
+ private void hideAlarmText() {
+ if (mNextAlarmView.getVisibility() == View.VISIBLE) {
+ mNextAlarmView.animate()
+ .alpha(0f)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Reset the alpha regardless of how the animation ends for the next
+ // time we show this view/want to animate it.
+ mNextAlarmView.setVisibility(View.INVISIBLE);
+ mNextAlarmView.setAlpha(1f);
+ mNextAlarmTextView.setText(null);
+ }
+ })
+ .start();
+ } else {
+ // Next alarm view is already hidden, only need to clear the text.
+ mNextAlarmTextView.setText(null);
+ }
+ }
+
public void updateEverything() {
post(() -> setClickable(false));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index bf9746e..77c3bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -23,7 +23,6 @@
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.AlarmTile;
import com.android.systemui.qs.tiles.BatterySaverTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
@@ -70,7 +69,6 @@
else if (tileSpec.equals("saver")) return new DataSaverTile(mHost);
else if (tileSpec.equals("night")) return new NightDisplayTile(mHost);
else if (tileSpec.equals("nfc")) return new NfcTile(mHost);
- else if (tileSpec.equals("alarm")) return new AlarmTile(mHost);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec);
else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 016cbd6..04dbb88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -50,7 +50,7 @@
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTooltipView;
+import com.android.systemui.qs.QuickStatusBarHeader;
import java.util.ArrayList;
@@ -197,7 +197,7 @@
Prefs.putInt(
mContext,
Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT);
+ QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
deleted file mode 100644
index ff3fe73..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import static android.service.quicksettings.Tile.STATE_ACTIVE;
-import static android.service.quicksettings.Tile.STATE_UNAVAILABLE;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.QS_ALARM;
-import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm;
-
-import android.app.AlarmManager.AlarmClockInfo;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.provider.AlarmClock;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-
-public class AlarmTile extends QSTileImpl implements NextAlarmChangeCallback {
- private final NextAlarmController mController;
- private String mNextAlarm;
- private PendingIntent mIntent;
-
- public AlarmTile(QSTileHost host) {
- super(host);
- mController = Dependency.get(NextAlarmController.class);
- }
-
- @Override
- public State newTileState() {
- return new BooleanState();
- }
-
- @Override
- protected void handleClick() {
- if (mIntent != null) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(mIntent);
- }
- }
-
- @Override
- protected void handleUpdateState(State state, Object arg) {
- state.state = mNextAlarm != null ? STATE_ACTIVE : STATE_UNAVAILABLE;
- state.label = getTileLabel();
- state.secondaryLabel = mNextAlarm;
- state.icon = ResourceIcon.get(R.drawable.stat_sys_alarm);
- ((BooleanState) state).value = mNextAlarm != null;
- }
-
- @Override
- public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
- if (nextAlarm != null) {
- mNextAlarm = formatNextAlarm(mContext, nextAlarm);
- mIntent = nextAlarm.getShowIntent();
- } else {
- mNextAlarm = null;
- mIntent = null;
- }
- refreshState();
- }
-
- @Override
- public int getMetricsCategory() {
- return QS_ALARM;
- }
-
- @Override
- public Intent getLongClickIntent() {
- return new Intent(AlarmClock.ACTION_SET_ALARM);
- }
-
- @Override
- protected void handleSetListening(boolean listening) {
- if (listening) {
- mController.addCallback(this);
- } else {
- mController.removeCallback(this);
- }
- }
-
- @Override
- public CharSequence getTileLabel() {
- return mContext.getString(R.string.status_bar_alarm);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 09acf3e..2375b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -87,10 +87,10 @@
super.onFinishInflate();
mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
mSystemIconsContainer = findViewById(R.id.system_icons_container);
- mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
- mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
- mCarrierLabel = (TextView) findViewById(R.id.keyguard_carrier_text);
- mBatteryView = (BatteryMeterView) mSystemIconsContainer.findViewById(R.id.battery);
+ mMultiUserSwitch = findViewById(R.id.multi_user_switch);
+ mMultiUserAvatar = findViewById(R.id.multi_user_avatar);
+ mCarrierLabel = findViewById(R.id.keyguard_carrier_text);
+ mBatteryView = mSystemIconsContainer.findViewById(R.id.battery);
loadDimens();
updateUserSwitcher();
@@ -220,7 +220,7 @@
Dependency.get(ConfigurationController.class).addCallback(this);
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
- onOverlayChanged();
+ onThemeChanged();
}
@Override
@@ -291,12 +291,9 @@
.setDuration(300)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mMultiUserSwitch.setAlpha(1f);
- getOverlay().remove(mMultiUserSwitch);
- }
+ .withEndAction(() -> {
+ mMultiUserSwitch.setAlpha(1f);
+ getOverlay().remove(mMultiUserSwitch);
})
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 840f55c..62151cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -115,6 +115,8 @@
private final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
private final static int ROTATE_BUTTON_LOOP_DURATION_MS = 2000;
+ private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
+
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -156,6 +158,7 @@
private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
private Animator mRotateHideAnimator;
+ private ViewRippler mViewRippler = new ViewRippler();
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
@@ -464,12 +467,16 @@
animIcon.start();
}
+ if (!isRotateSuggestionIntroduced()) mViewRippler.start(view);
+
// Set visibility, may fail if a11y service is active.
// If invisible, call will stop animation.
mNavigationBarView.setRotateButtonVisibility(true);
} else { // Hide
+ mViewRippler.stop(); // Prevent any pending ripples, force hide or not
+
if (force) {
// If a hide animator is running stop it and make invisible
if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
@@ -519,6 +526,25 @@
return 6000;
}
+ private boolean isRotateSuggestionIntroduced() {
+ ContentResolver cr = getContext().getContentResolver();
+ return Settings.Secure.getInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0)
+ >= NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION;
+ }
+
+ private void incrementNumAcceptedRotationSuggestionsIfNeeded() {
+ // Get the number of accepted suggestions
+ ContentResolver cr = getContext().getContentResolver();
+ final int numSuggestions = Settings.Secure.getInt(cr,
+ Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0);
+
+ // Increment the number of accepted suggestions only if it would change intro mode
+ if (numSuggestions < NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION) {
+ Settings.Secure.putInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
+ numSuggestions + 1);
+ }
+ }
+
// Injected from StatusBar at creation.
public void setCurrentSysuiVisibility(int systemUiVisibility) {
mSystemUiVisibility = systemUiVisibility;
@@ -861,6 +887,7 @@
private void onRotateSuggestionClick(View v) {
mMetricsLogger.action(MetricsEvent.ACTION_ROTATION_SUGGESTION_ACCEPTED);
+ incrementNumAcceptedRotationSuggestionsIfNeeded();
mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
}
@@ -989,6 +1016,35 @@
}
}
+ private class ViewRippler {
+ private static final int RIPPLE_OFFSET_MS = 50;
+ private static final int RIPPLE_INTERVAL_MS = 2000;
+ private View mRoot;
+
+ public void start(View root) {
+ stop(); // Stop any pending ripple animations
+
+ mRoot = root;
+
+ // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 2*RIPPLE_INTERVAL_MS);
+ }
+
+ public void stop() {
+ if (mRoot != null) mRoot.removeCallbacks(mRipple);
+ }
+
+ private final Runnable mRipple = new Runnable() {
+ @Override
+ public void run() { // Cause the ripple to fire via false presses
+ mRoot.setPressed(true);
+ mRoot.setPressed(false);
+ }
+ };
+ }
+
public static View create(Context context, FragmentListener listener) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 57d78dd..320b56f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -149,7 +149,7 @@
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning()) {
+ if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
return false;
}
@@ -182,7 +182,7 @@
}
public boolean onTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning()) {
+ if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 51d094e..9d2480b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -205,7 +205,6 @@
private int mQsFalsingThreshold;
private float mKeyguardStatusBarAnimateAlpha = 1f;
- private float mQsClockAlphaOverride = 1f;
private int mOldLayoutDirection;
private HeadsUpTouchHelper mHeadsUpTouchHelper;
private boolean mIsExpansionFromHeadsUp;
@@ -583,7 +582,7 @@
private void updateClock() {
if (!mKeyguardStatusViewAnimating) {
- mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha * mQsClockAlphaOverride);
+ mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
}
}
@@ -1310,15 +1309,6 @@
mQsNavbarScrim.setAlpha(getQsExpansionFraction());
}
- // Fade clock when QS is on top of it
- float newClockAlpha = (height - mKeyguardStatusView.getY()) /
- mKeyguardStatusView.getHeight();
- newClockAlpha = 1 - MathUtils.constrain(newClockAlpha, 0, 1);
- if (newClockAlpha != mQsClockAlphaOverride) {
- mQsClockAlphaOverride = Interpolators.ALPHA_OUT.getInterpolation(newClockAlpha);
- updateClock();
- }
-
if (mAccessibilityManager.isEnabled()) {
setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 747a551..c326fee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -762,20 +762,31 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
- action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
- updateVolumeZen();
- } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- updateSimState(intent);
- } else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
- updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
- TelecomManager.TTY_MODE_OFF));
- } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) ||
- action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) ||
- action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) {
- updateManagedProfile();
- } else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
- updateHeadsetPlug(intent);
+ switch (action) {
+ case AudioManager.RINGER_MODE_CHANGED_ACTION:
+ case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
+ updateVolumeZen();
+ break;
+ case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ // Avoid rebroadcast because SysUI is direct boot aware.
+ if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
+ false)) {
+ break;
+ }
+ updateSimState(intent);
+ break;
+ case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
+ updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
+ TelecomManager.TTY_MODE_OFF));
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+ updateManagedProfile();
+ break;
+ case AudioManager.ACTION_HEADSET_PLUG:
+ updateHeadsetPlug(intent);
+ break;
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 255e5e4..d8d388c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -167,6 +167,7 @@
private Callback mCallback;
private boolean mWallpaperSupportsAmbientMode;
private boolean mScreenOn;
+ private float mNotificationDensity;
// Scrim blanking callbacks
private Choreographer.FrameCallback mPendingFrameCallback;
@@ -251,7 +252,7 @@
mCurrentInFrontTint = state.getFrontTint();
mCurrentBehindTint = state.getBehindTint();
mCurrentInFrontAlpha = state.getFrontAlpha();
- mCurrentBehindAlpha = state.getBehindAlpha();
+ mCurrentBehindAlpha = state.getBehindAlpha(mNotificationDensity);
applyExpansionToAlpha();
// Cancel blanking transitions that were pending before we requested a new state
@@ -396,12 +397,13 @@
// Either darken of make the scrim transparent when you
// pull down the shade
float interpolatedFract = getInterpolatedFraction();
+ float alphaBehind = mState.getBehindAlpha(mNotificationDensity);
if (mDarkenWhileDragging) {
- mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
- mScrimBehindAlphaKeyguard, interpolatedFract);
+ mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking, alphaBehind,
+ interpolatedFract);
mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
} else {
- mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+ mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind,
interpolatedFract);
mCurrentInFrontAlpha = 0;
}
@@ -415,15 +417,14 @@
public void setNotificationCount(int notificationCount) {
final float maxNotificationDensity = 3;
float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
- float newAlpha = MathUtils.map(0, 1,
- GRADIENT_SCRIM_ALPHA, GRADIENT_SCRIM_ALPHA_BUSY,
- notificationDensity);
- if (mScrimBehindAlphaKeyguard != newAlpha) {
- mScrimBehindAlphaKeyguard = newAlpha;
+ if (mNotificationDensity == notificationDensity) {
+ return;
+ }
+ mNotificationDensity = notificationDensity;
- if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER) {
- scheduleUpdate();
- }
+ if (mState == ScrimState.KEYGUARD) {
+ applyExpansionToAlpha();
+ scheduleUpdate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 381e4af..053c5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -18,6 +18,7 @@
import android.graphics.Color;
import android.os.Trace;
+import android.util.MathUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.ScrimView;
@@ -55,6 +56,13 @@
mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
mCurrentInFrontAlpha = 0;
}
+
+ @Override
+ public float getBehindAlpha(float busynessFactor) {
+ return MathUtils.map(0 /* start */, 1 /* stop */,
+ ScrimController.GRADIENT_SCRIM_ALPHA, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+ busynessFactor);
+ }
},
/**
@@ -183,7 +191,7 @@
return mCurrentInFrontAlpha;
}
- public float getBehindAlpha() {
+ public float getBehindAlpha(float busyness) {
return mCurrentBehindAlpha;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index baf0ebf..5363742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -403,54 +403,62 @@
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
- if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
- action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
- updateConnectivity();
- } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
- refreshLocale();
- updateAirplaneMode(false);
- } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
- // We are using different subs now, we might be able to make calls.
- recalculateEmergency();
- } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
- // Notify every MobileSignalController so they can know whether they are the
- // data sim or not.
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController controller = mMobileSignalControllers.valueAt(i);
- controller.handleBroadcast(intent);
- }
- } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- // Might have different subscriptions now.
- updateMobileControllers();
- } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
- mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
- if (mMobileSignalControllers.size() == 0) {
- // If none of the subscriptions are active, we might need to recalculate
- // emergency state.
+ switch (action) {
+ case ConnectivityManager.CONNECTIVITY_ACTION:
+ case ConnectivityManager.INET_CONDITION_ACTION:
+ updateConnectivity();
+ break;
+ case Intent.ACTION_AIRPLANE_MODE_CHANGED:
+ refreshLocale();
+ updateAirplaneMode(false);
+ break;
+ case TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ // We are using different subs now, we might be able to make calls.
recalculateEmergency();
- }
- } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- mConfig = Config.readConfig(mContext);
- mReceiverHandler.post(new Runnable() {
- @Override
- public void run() {
- handleConfigurationChanged();
+ break;
+ case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ // Notify every MobileSignalController so they can know whether they are the
+ // data sim or not.
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
+ controller.handleBroadcast(intent);
}
- });
- } else {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
- mMobileSignalControllers.get(subId).handleBroadcast(intent);
+ break;
+ case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ // Avoid rebroadcast because SysUI is direct boot aware.
+ if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ break;
+ }
+ // Might have different subscriptions now.
+ updateMobileControllers();
+ break;
+ case TelephonyIntents.ACTION_SERVICE_STATE_CHANGED:
+ mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
+ if (mMobileSignalControllers.size() == 0) {
+ // If none of the subscriptions are active, we might need to recalculate
+ // emergency state.
+ recalculateEmergency();
+ }
+ break;
+ case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+ mConfig = Config.readConfig(mContext);
+ mReceiverHandler.post(this::handleConfigurationChanged);
+ break;
+ default:
+ int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
+ mMobileSignalControllers.get(subId).handleBroadcast(intent);
+ } else {
+ // Can't find this subscription... We must be out of date.
+ updateMobileControllers();
+ }
} else {
- // Can't find this subscription... We must be out of date.
- updateMobileControllers();
+ // No sub id, must be for the wifi.
+ mWifiSignalController.handleBroadcast(intent);
}
- } else {
- // No sub id, must be for the wifi.
- mWifiSignalController.handleBroadcast(intent);
- }
+ break;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
new file mode 100644
index 0000000..21483aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import android.content.Intent;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardUpdateMonitorTest extends SysuiTestCase {
+
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setup() {
+ mTestableLooper = TestableLooper.get(this);
+ }
+
+ @Test
+ public void testIgnoresSimStateCallback_rebroadcast() {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+
+ AtomicBoolean simStateChanged = new AtomicBoolean(false);
+ KeyguardUpdateMonitor keyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
+ @Override
+ protected void handleSimStateChange(int subId, int slotId,
+ IccCardConstants.State state) {
+ simStateChanged.set(true);
+ super.handleSimStateChange(subId, slotId, state);
+ }
+ };
+
+ keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+ mTestableLooper.processAllMessages();
+ Assert.assertTrue("onSimStateChanged not called", simStateChanged.get());
+
+ intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+ simStateChanged.set(false);
+ keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+ mTestableLooper.processAllMessages();
+ Assert.assertFalse("onSimStateChanged should have been skipped", simStateChanged.get());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 8347fb0..168d8d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -356,6 +356,52 @@
Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
}
+ /**
+ * Number of visible notifications affects scrim opacity.
+ */
+ @Test
+ public void testNotificationDensity() {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.finishAnimationsImmediately();
+
+ mScrimController.setNotificationCount(0);
+ mScrimController.finishAnimationsImmediately();
+ Assert.assertEquals("lower density when no notifications",
+ ScrimController.GRADIENT_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.01f);
+
+ mScrimController.setNotificationCount(3);
+ mScrimController.finishAnimationsImmediately();
+ Assert.assertEquals("stronger density when notifications are visible",
+ ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mScrimBehind.getViewAlpha(), 0.01f);
+ }
+
+ /**
+ * Moving from/to states conserves old notification density.
+ */
+ @Test
+ public void testConservesNotificationDensity() {
+ testConservesNotificationDensity(0 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA);
+ testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY);
+ }
+
+ /**
+ * Conserves old notification density after leaving state and coming back.
+ *
+ * @param count How many notification.
+ * @param expectedAlpha Expected alpha.
+ */
+ private void testConservesNotificationDensity(int count, float expectedAlpha) {
+ mScrimController.setNotificationCount(count);
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.finishAnimationsImmediately();
+
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.finishAnimationsImmediately();
+
+ Assert.assertEquals("Doesn't respect notification busyness after transition",
+ expectedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+ }
+
private void assertScrimTint(ScrimView scrimView, boolean tinted) {
final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 50968a0..b0b9586 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -128,6 +128,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.IntSupplier;
@@ -2055,7 +2056,7 @@
}
ComponentName componentName = ComponentName.unflattenFromString(componentId);
- if (componentName.equals(userState.mServiceAssignedToAccessibilityButton)) {
+ if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) {
return false;
}
userState.mServiceAssignedToAccessibilityButton = componentName;
@@ -3300,7 +3301,8 @@
if (mAccessibilityFocusedWindowId != windowId) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::clearAccessibilityFocus,
- AccessibilityManagerService.this, 0));
+ AccessibilityManagerService.this,
+ box(mAccessibilityFocusedWindowId)));
mSecurityPolicy.setAccessibilityFocusedWindowLocked(windowId);
mAccessibilityFocusNodeId = nodeId;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3b2a22d..2251d2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -62,6 +62,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
@@ -1638,6 +1639,11 @@
}
int runSwitchUser(PrintWriter pw) throws RemoteException {
+ UserManager userManager = mInternal.mContext.getSystemService(UserManager.class);
+ if (!userManager.canSwitchUsers()) {
+ getErrPrintWriter().println("Error: disallowed switching user");
+ return -1;
+ }
String user = getNextArgRequired();
mInterface.switchUser(Integer.parseInt(user));
return 0;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 4b432a3a..b110e88 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -521,10 +521,10 @@
synchronized (mLock) {
// List to keep the session services that need be removed because they don't exist
// in the 'services' above.
- Set<SessionToken2> sessionTokensToRemove = new HashSet<>(mSessionRecords.keySet());
- for (SessionToken2 token : sessionTokensToRemove) {
- if (token.getType() == TYPE_SESSION) {
- sessionTokensToRemove.remove(token);
+ Set<SessionToken2> sessionTokensToRemove = new HashSet<>();
+ for (SessionToken2 token : mSessionRecords.keySet()) {
+ if (token.getType() != TYPE_SESSION) {
+ sessionTokensToRemove.add(token);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 8591304..e315bc5 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -146,6 +146,12 @@
Intent intent = registerReceiver(null, filter);
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
+
+ if (!present) {
+ // No battery, treat as if 100%, no possibility of draining battery.
+ return 100;
+ }
if (level < 0 || scale <= 0) {
// Battery data unavailable. This should never happen, so assume the worst.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e40dc4f..7efc9876 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -785,6 +785,9 @@
private int mCurrentUserId;
+ /* Whether accessibility is magnifying the screen */
+ private boolean mScreenMagnificationActive;
+
// Maps global key codes to the components that will handle them.
private GlobalKeyManager mGlobalKeyManager;
@@ -8164,7 +8167,11 @@
*/
private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
boolean freeformStackVisible, boolean isDockedDividerResizing) {
- if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
+ if (mScreenMagnificationActive) {
+ // When the screen is magnified, the nav bar should be opaque since its background
+ // can vary as the user pans and zooms
+ visibility = setNavBarOpaqueFlag(visibility);
+ } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
visibility = setNavBarOpaqueFlag(visibility);
}
@@ -8319,6 +8326,14 @@
}
@Override
+ public void onScreenMagnificationStateChanged(boolean active) {
+ synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
+ mScreenMagnificationActive = active;
+ updateSystemUiVisibilityLw();
+ }
+ }
+
+ @Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index dde4bc8..bf0c3da 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1700,6 +1700,13 @@
boolean canDismissBootAnimation();
/**
+ * Called when the magnification state changes.
+ *
+ * @param active Whether magnification is active (that is, we are zoomed in).
+ */
+ void onScreenMagnificationStateChanged(boolean active);
+
+ /**
* Convert the user rotation mode to a human readable format.
*/
static String userRotationModeToString(int mode) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 659253f..c31cdec 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -87,6 +87,8 @@
private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
+ private boolean mScreenMagnificationActive;
+
public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
if (callbacks != null) {
if (mDisplayMagnifier != null) {
@@ -136,6 +138,11 @@
if (mWindowsForAccessibilityObserver != null) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
+ boolean nowActive = !spec.isNop();
+ if (nowActive != mScreenMagnificationActive) {
+ mScreenMagnificationActive = nowActive;
+ mService.mPolicy.onScreenMagnificationStateChanged(nowActive);
+ }
}
public void getMagnificationRegionLocked(Region outMagnificationRegion) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 0b99eaa..206ee7a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -644,4 +644,8 @@
public boolean canDismissBootAnimation() {
return true;
}
+
+ @Override
+ public void onScreenMagnificationStateChanged(boolean active) {
+ }
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 0874b86..8a3f138 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -395,6 +395,112 @@
}
/**
+ * Send a text based SMS with messaging options.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * {@hide}
+ */
+ public void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ true /* persistMessage*/, priority, expectMore, validityPeriod);
+ }
+
+ private void sendTextMessageInternal(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
+ int priority, boolean expectMore, int validityPeriod) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (priority < 0x00 || priority > 0x03) {
+ throw new IllegalArgumentException("Invalid priority");
+ }
+
+ if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ throw new IllegalArgumentException("Invalid validity period");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ if (iccISms != null) {
+ iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+ validityPeriod);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendTextMessage(String, String, String, PendingIntent,
+ * PendingIntent, int, boolean, int)
+ * @hide
+ */
+ public void sendTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
+ boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ false /* persistMessage */, priority, expectMore, validityPeriod);
+ }
+
+ /**
+ *
* Inject an SMS PDU into the android application framework.
*
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
@@ -552,6 +658,140 @@
}
/**
+ * Send a multi-part text based SMS with messaging options. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * <code>divideMessage</code>.
+ *
+ * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+ * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+ * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+ * writes messages sent using this method to the SMS Provider (the default SMS app is always
+ * responsible for writing its sent messages to the SMS Provider). For information about
+ * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * {@hide}
+ */
+ public void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, true /* persistMessage*/);
+ }
+
+ private void sendMultipartTextMessageInternal(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+ boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+ if (parts == null || parts.size() < 1) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (priority < 0x00 || priority > 0x03) {
+ throw new IllegalArgumentException("Invalid priority");
+ }
+
+ if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ throw new IllegalArgumentException("Invalid validity period");
+ }
+
+ if (parts.size() > 1) {
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ if (iccISms != null) {
+ iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress,
+ parts, sentIntents, deliveryIntents, persistMessage, priority,
+ expectMore, validityPeriod);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ } else {
+ PendingIntent sentIntent = null;
+ PendingIntent deliveryIntent = null;
+ if (sentIntents != null && sentIntents.size() > 0) {
+ sentIntent = sentIntents.get(0);
+ }
+ if (deliveryIntents != null && deliveryIntents.size() > 0) {
+ deliveryIntent = deliveryIntents.get(0);
+ }
+ sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
+ sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+ validityPeriod);
+ }
+ }
+
+ /**
+ * Send a multi-part text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
+ * ArrayList, int, boolean, int)
+ * @hide
+ **/
+ public void sendMultipartTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, false /* persistMessage*/, priority, expectMore,
+ validityPeriod);
+ }
+
+ /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1014,7 +1254,7 @@
* <code>getAllMessagesFromIcc</code>
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
- private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+ private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
if (records != null) {
int count = records.size();
@@ -1022,7 +1262,8 @@
SmsRawData data = records.get(i);
// List contains all records, including "free" records (null)
if (data != null) {
- SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+ SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+ getSubscriptionId());
if (sms != null) {
messages.add(sms);
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 577ea7d..9d03b59 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -277,6 +277,31 @@
}
/**
+ * Create an SmsMessage from an SMS EF record.
+ *
+ * @param index Index of SMS record. This should be index in ArrayList
+ * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param data Record data.
+ * @param subId Subscription Id of the SMS
+ * @return An SmsMessage representing the record.
+ *
+ * @hide
+ */
+ public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
+ SmsMessageBase wrappedMessage;
+
+ if (isCdmaVoice(subId)) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+ index, data);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+ index, data);
+ }
+
+ return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+ }
+
+ /**
* Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
* length in bytes (not hex chars) less the SMSC header
*
@@ -836,6 +861,7 @@
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
return (PHONE_TYPE_CDMA == activePhone);
}
+
/**
* Decide if the carrier supports long SMS.
* {@hide}
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 41c1430..e8597b2 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -17,6 +17,7 @@
package android.telephony.data;
import android.annotation.SystemApi;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -230,8 +231,10 @@
@Override
public String toString() {
- return "DataProfile=" + mProfileId + "/" + mApn + "/" + mProtocol + "/" + mAuthType
- + "/" + mUserName + "/" + mPassword + "/" + mType + "/" + mMaxConnsTime
+ return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
+ + "/" + (Build.IS_USER ? "***/***/***" :
+ (mApn + "/" + mUserName + "/" + mPassword))
+ + "/" + mType + "/" + mMaxConnsTime
+ "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
+ mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
+ mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 0673a38..0664a7e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -74,7 +74,9 @@
/** @hide */
@IntDef({
DELIVER_STATUS_OK,
- DELIVER_STATUS_ERROR
+ DELIVER_STATUS_ERROR_GENERIC,
+ DELIVER_STATUS_ERROR_NO_MEMORY,
+ DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeliverStatusResult {}
@@ -86,7 +88,17 @@
/**
* Message was not delivered.
*/
- public static final int DELIVER_STATUS_ERROR = 2;
+ public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
+
+ /**
+ * Message was not delivered due to lack of memory.
+ */
+ public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
+
+ /**
+ * Message was not delivered as the request is not supported.
+ */
+ public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
/** @hide */
@IntDef({
@@ -106,7 +118,6 @@
*/
public static final int STATUS_REPORT_STATUS_ERROR = 2;
-
// Lock for feature synchronization
private final Object mLock = new Object();
private IImsSmsListener mListener;
@@ -157,7 +168,9 @@
* @param token token provided in {@link #onSmsReceived(int, String, byte[])}
* @param result result of delivering the message. Valid values are:
* {@link #DELIVER_STATUS_OK},
- * {@link #DELIVER_STATUS_ERROR}
+ * {@link #DELIVER_STATUS_ERROR_GENERIC},
+ * {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
+ * {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
* @param messageRef the message reference
*/
public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
@@ -200,7 +213,7 @@
mListener.onSmsReceived(token, format, pdu);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
- acknowledgeSms(token, 0, DELIVER_STATUS_ERROR);
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index fe37531..a4eb424 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -187,6 +187,57 @@
in PendingIntent deliveryIntent, in boolean persistMessage);
/**
+ * Send an SMS with options using Subscription Id.
+ *
+ * @param subId the subId on which the SMS has to be sent.
+ * @param destAddr the address to send the message to
+ * @param scAddr the SMSC to send the message through, or NULL for the
+ * default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ */
+ void sendTextForSubscriberWithOptions(in int subId, String callingPkg, in String destAddr,
+ in String scAddr, in String text, in PendingIntent sentIntent,
+ in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp,
+ in int priority, in boolean expectMore, in int validityPeriod);
+
+ /**
* Inject an SMS PDU into the android platform.
*
* @param subId the subId on which the SMS has to be injected.
@@ -234,6 +285,56 @@
in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp);
/**
+ * Send a multi-part text based SMS with options using Subscription Id.
+ *
+ * @param subId the subId on which the SMS has to be sent.
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ */
+ void sendMultipartTextForSubscriberWithOptions(in int subId, String callingPkg,
+ in String destinationAddress, in String scAddress, in List<String> parts,
+ in List<PendingIntent> sentIntents, in List<PendingIntent> deliveryIntents,
+ in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore,
+ in int validityPeriod);
+
+ /**
* Enable reception of cell broadcast (SMS-CB) messages with the given
* message identifier and RAN type. The RAN type specify this message ID
* belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 7a53ef6..14c5f4b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -99,6 +99,15 @@
private static final int RETURN_NO_ACK = 0;
private static final int RETURN_ACK = 1;
+ /**
+ * Supported priority modes for CDMA SMS messages
+ * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
+ */
+ private static final int PRIORITY_NORMAL = 0x0;
+ private static final int PRIORITY_INTERACTIVE = 0x1;
+ private static final int PRIORITY_URGENT = 0x2;
+ private static final int PRIORITY_EMERGENCY = 0x3;
+
private SmsEnvelope mEnvelope;
private BearerData mBearerData;
@@ -211,6 +220,26 @@
*/
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
boolean statusReportRequested, SmsHeader smsHeader) {
+ return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddr Service Centre address. Null means use default.
+ * @param destAddr Address of the recipient.
+ * @param message String representation of the message payload.
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @param smsHeader Array containing the data for the User Data Header, preceded
+ * by the Element Identifiers.
+ * @param priority Priority level of the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
+ boolean statusReportRequested, SmsHeader smsHeader, int priority) {
/**
* TODO(cleanup): Do we really want silent failure like this?
@@ -224,7 +253,7 @@
UserData uData = new UserData();
uData.payloadStr = message;
uData.userDataHeader = smsHeader;
- return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
+ return privateGetSubmitPdu(destAddr, statusReportRequested, uData, priority);
}
/**
@@ -282,6 +311,22 @@
}
/**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ *
+ * @param destAddr the address of the destination for the message
+ * @param userData the data for the message
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @param priority Priority level of the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
+ boolean statusReportRequested, int priority) {
+ return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority);
+ }
+
+ /**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
@Override
@@ -764,6 +809,15 @@
*/
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
UserData userData) {
+ return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1);
+ }
+
+ /**
+ * Creates BearerData and Envelope from parameters for a Submit SMS.
+ * @return byte stream for SubmitPdu.
+ */
+ private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
+ UserData userData, int priority) {
/**
* TODO(cleanup): give this function a more meaningful name.
@@ -792,6 +846,10 @@
bearerData.userAckReq = false;
bearerData.readAckReq = false;
bearerData.reportReq = false;
+ if (priority >= PRIORITY_NORMAL && priority <= PRIORITY_EMERGENCY) {
+ bearerData.priorityIndicatorSet = true;
+ bearerData.priority = priority;
+ }
bearerData.userData = userData;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 1ca19e0..4f5bfa9 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -89,6 +89,18 @@
private int mVoiceMailCount = 0;
+ private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
+ private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
+ private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
+ private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
+
+ //Validity Period min - 5 mins
+ private static final int VALIDITY_PERIOD_MIN = 5;
+ //Validity Period max - 63 weeks
+ private static final int VALIDITY_PERIOD_MAX = 635040;
+
+ private static final int INVALID_VALIDITY_PERIOD = -1;
+
public static class SubmitPdu extends SubmitPduBase {
}
@@ -202,6 +214,45 @@
}
/**
+ * Get Encoded Relative Validty Period Value from Validity period in mins.
+ *
+ * @param validityPeriod Validity period in mins.
+ *
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * ||relValidityPeriod (TP-VP) || || validityPeriod ||
+ *
+ * 0 to 143 ---> (TP-VP + 1) x 5 minutes
+ *
+ * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes)
+ *
+ * 168 to 196 ---> (TP-VP - 166) x 1 day
+ *
+ * 197 to 255 ---> (TP-VP - 192) x 1 week
+ *
+ * @return relValidityPeriod Encoded Relative Validity Period Value.
+ * @hide
+ */
+ public static int getRelativeValidityPeriod(int validityPeriod) {
+ int relValidityPeriod = INVALID_VALIDITY_PERIOD;
+
+ if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) {
+ Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
+ return relValidityPeriod;
+ }
+
+ if (validityPeriod <= 720) {
+ relValidityPeriod = (validityPeriod / 5) - 1;
+ } else if (validityPeriod <= 1440) {
+ relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
+ } else if (validityPeriod <= 43200) {
+ relValidityPeriod = (validityPeriod / 1440) + 166;
+ } else if (validityPeriod <= 635040) {
+ relValidityPeriod = (validityPeriod / 10080) + 192;
+ }
+ return relValidityPeriod;
+ }
+
+ /**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddress Service Centre address. Null means use default.
@@ -236,6 +287,29 @@
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
+ header, encoding, languageTable, languageShiftTable, -1);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message using the
+ * specified encoding.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param encoding Encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param languageTable
+ * @param languageShiftTable
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header, int encoding,
+ int languageTable, int languageShiftTable, int validityPeriod) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
@@ -272,8 +346,19 @@
}
SubmitPdu ret = new SubmitPdu();
- // MTI = SMS-SUBMIT, UDHI = header != null
- byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
+
+ int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
+ int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
+
+ // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
+ //bit 4:3 = 10 - TP-VP field present - relative format
+ if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
+ validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
+ }
+
+ byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
+ (header != null ? 0x40 : 0x00));
+
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
statusReportRequested, ret);
@@ -338,7 +423,11 @@
bo.write(0x08);
}
- // (no TP-Validity-Period)
+ if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
+ // ( TP-Validity-Period - relative format)
+ bo.write(relativeValidityPeriod);
+ }
+
bo.write(userData, 0, userData.length);
ret.encodedMessage = bo.toByteArray();
return ret;
@@ -388,6 +477,24 @@
}
/**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message
+ * @param statusReportRequested staus report of the message Requested
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, int validityPeriod) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
+ null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
+ }
+
+ /**
* Get an SMS-SUBMIT PDU for a data message to a destination address & port
*
* @param scAddress Service Centre address. null == use default
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
index 2686290..1560262 100644
--- a/tests/ActivityManagerPerfTests/README.txt
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -49,5 +49,7 @@
can be reported in an iteration
* If the target package should be running before your test logic starts, add startTargetPackage();
at the beginning of the iteration
+
* Reporting
- * Look at go/am-perf for how to add new tests to dashboards and receive notification on regression
+ * Look at internal documentation for how to add new tests to dashboards and receive notification
+ on regressions
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index b5273dd..50c4b5e 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -999,6 +999,7 @@
// just in case legacy API needed some relatively real timestamp
legacyResults[i].ts = SystemClock.elapsedRealtime() * 1000;
}
+ i++;
}
listener.onSuccess(legacyResults);
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index b573adf..c5ad7de 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -263,6 +263,7 @@
*
* @throws Exception
*/
+ @Test
public void validateCertCredentialWithoutCaCert() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
cred.setCaCertificate(null);