Merge "Support Signal strength offset"
diff --git a/api/current.txt b/api/current.txt
index 77cb9e4..8ef77a1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21827,7 +21827,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -22379,7 +22379,7 @@
method public boolean advance();
method public long getCachedDuration();
method public android.media.DrmInitData getDrmInitData();
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -22634,6 +22634,69 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
+ public final class MediaMetricsSet {
+ method public double getDouble(java.lang.String, double);
+ method public int getInt(java.lang.String, int);
+ method public long getLong(java.lang.String, long);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public boolean isEmpty();
+ method public java.util.Set<java.lang.String> keySet();
+ method public int size();
+ }
+
+ public static final class MediaMetricsSet.MediaCodec {
+ field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+ field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ }
+
+ public static final class MediaMetricsSet.MediaExtractor {
+ field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+ field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
+ public static final class MediaMetricsSet.MediaPlayer {
+ field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+ }
+
+ public static final class MediaMetricsSet.MediaRecorder {
+ field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+ }
+
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22674,7 +22737,7 @@
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -22842,7 +22905,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -24653,7 +24716,7 @@
public final class TvInputInfo implements android.os.Parcelable {
method public boolean canRecord();
- method public android.content.Intent createSettingsIntent();
+ method public deprecated android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
method public android.os.Bundle getExtras();
@@ -33878,6 +33941,7 @@
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+ field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -39812,7 +39876,6 @@
method public android.os.PersistableBundle getCarrierConfig();
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
- method public boolean getDataEnabled();
method public int getDataNetworkType();
method public int getDataState();
method public java.lang.String getDeviceId();
@@ -39853,6 +39916,7 @@
method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataAllowed();
+ method public boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
diff --git a/api/system-current.txt b/api/system-current.txt
index 827b46c..d80591b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -23623,7 +23623,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -24175,7 +24175,7 @@
method public boolean advance();
method public long getCachedDuration();
method public android.media.DrmInitData getDrmInitData();
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -24430,6 +24430,69 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
+ public final class MediaMetricsSet {
+ method public double getDouble(java.lang.String, double);
+ method public int getInt(java.lang.String, int);
+ method public long getLong(java.lang.String, long);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public boolean isEmpty();
+ method public java.util.Set<java.lang.String> keySet();
+ method public int size();
+ }
+
+ public static final class MediaMetricsSet.MediaCodec {
+ field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+ field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ }
+
+ public static final class MediaMetricsSet.MediaExtractor {
+ field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+ field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
+ public static final class MediaMetricsSet.MediaPlayer {
+ field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+ }
+
+ public static final class MediaMetricsSet.MediaRecorder {
+ field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+ }
+
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -24470,7 +24533,7 @@
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -24638,7 +24701,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -26653,7 +26716,7 @@
public final class TvInputInfo implements android.os.Parcelable {
method public boolean canRecord();
- method public android.content.Intent createSettingsIntent();
+ method public deprecated android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -36758,6 +36821,7 @@
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+ field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -43216,8 +43280,8 @@
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
method public int getDataActivity();
- method public boolean getDataEnabled();
- method public boolean getDataEnabled(int);
+ method public deprecated boolean getDataEnabled();
+ method public deprecated boolean getDataEnabled(int);
method public int getDataNetworkType();
method public int getDataState();
method public java.lang.String getDeviceId();
@@ -43264,6 +43328,7 @@
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataAllowed();
method public boolean isDataConnectivityPossible();
+ method public boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isIdle();
method public boolean isNetworkRoaming();
diff --git a/api/test-current.txt b/api/test-current.txt
index 04f4f21..66beae7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -21928,7 +21928,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -22480,7 +22480,7 @@
method public boolean advance();
method public long getCachedDuration();
method public android.media.DrmInitData getDrmInitData();
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -22735,6 +22735,69 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
+ public final class MediaMetricsSet {
+ method public double getDouble(java.lang.String, double);
+ method public int getInt(java.lang.String, int);
+ method public long getLong(java.lang.String, long);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public boolean isEmpty();
+ method public java.util.Set<java.lang.String> keySet();
+ method public int size();
+ }
+
+ public static final class MediaMetricsSet.MediaCodec {
+ field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+ field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ }
+
+ public static final class MediaMetricsSet.MediaExtractor {
+ field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+ field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
+ public static final class MediaMetricsSet.MediaPlayer {
+ field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+ }
+
+ public static final class MediaMetricsSet.MediaRecorder {
+ field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+ }
+
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22775,7 +22838,7 @@
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -22943,7 +23006,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.os.Bundle getMetrics();
+ method public android.media.MediaMetricsSet getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -24754,7 +24817,7 @@
public final class TvInputInfo implements android.os.Parcelable {
method public boolean canRecord();
- method public android.content.Intent createSettingsIntent();
+ method public deprecated android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
method public android.os.Bundle getExtras();
@@ -34005,6 +34068,7 @@
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+ field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -40001,7 +40065,6 @@
method public android.os.PersistableBundle getCarrierConfig();
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
- method public boolean getDataEnabled();
method public int getDataNetworkType();
method public int getDataState();
method public java.lang.String getDeviceId();
@@ -40042,6 +40105,7 @@
method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataAllowed();
+ method public boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index be38f42..77c4c7e 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -597,8 +597,7 @@
// Avoid the binder call when the package is the current application package.
// The activity manager will perform ensure that dexopt is performed before
// spinning up the process.
- if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
- VMRuntime.getRuntime().vmInstructionSet();
+ if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
try {
ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index d428a3a..2f87633 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -24,6 +24,7 @@
import android.database.CursorToBulkCursorAdaptor;
import android.database.DatabaseUtils;
import android.database.IContentObserver;
+import android.database.PageViewCursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -103,6 +104,7 @@
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
+ cursor = PageViewCursor.wrap(cursor, queryArgs);
try {
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java
new file mode 100644
index 0000000..fbd039d
--- /dev/null
+++ b/core/java/android/database/PageViewCursor.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.database;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.MathUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Cursor wrapper that provides visibility into a subset of a wrapped cursor.
+ *
+ * The window is specified by offset and limit.
+ *
+ * @hide
+ */
+public final class PageViewCursor extends CrossProcessCursorWrapper {
+
+ /**
+ * An extra added to results that are auto-paged using the wrapper.
+ */
+ public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
+
+ private static final String TAG = "PageViewCursor";
+ private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
+
+ private final int mOffset; // aka first index
+ private final int mCount;
+ private final Bundle mExtras;
+
+ private int mPos = -1;
+
+ /**
+ * @see PageViewCursor#wrap(Cursor, Bundle)
+ */
+ @VisibleForTesting
+ public PageViewCursor(Cursor cursor, int offset, int limit) {
+ super(cursor);
+
+ checkArgument(offset > -1);
+ checkArgument(limit > -1);
+
+ mOffset = offset;
+
+ mExtras = new Bundle();
+ Bundle extras = cursor.getExtras();
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
+ mExtras.putBoolean(EXTRA_AUTO_PAGED, true);
+
+ // We need a mutable bundle so we can add QUERY_RESULT_SIZE.
+ // Direct equality check is correct here. Bundle.EMPTY is a specific instance
+ // of Bundle that is immutable by way of implementation.
+ // mExtras = (extras == Bundle.EMPTY) ? new Bundle() : extras;
+
+ // When we're wrapping another cursor, it should not already be "paged".
+ checkArgument(!mExtras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE));
+
+ int count = mCursor.getCount();
+ mExtras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, count);
+
+ mCount = MathUtils.constrain(count - offset, 0, limit);
+
+ if (DEBUG) Log.d(TAG, "Wrapped cursor"
+ + " offset: " + mOffset
+ + ", limit: " + limit
+ + ", delegate_size: " + count
+ + ", paged_count: " + mCount);
+ }
+
+ @Override
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPos;
+ }
+
+ @Override
+ public boolean isBeforeFirst() {
+ if (mCount == 0) {
+ return true;
+ }
+ return mPos == -1;
+ }
+
+ @Override
+ public boolean isAfterLast() {
+ if (mCount == 0) {
+ return true;
+ }
+ return mPos == mCount;
+ }
+
+ @Override
+ public boolean isFirst() {
+ return mPos == 0;
+ }
+
+ @Override
+ public boolean isLast() {
+ return mPos == mCount - 1;
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ return moveToPosition(0);
+ }
+
+ @Override
+ public boolean moveToLast() {
+ return moveToPosition(mCount - 1);
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return move(1);
+ }
+
+ @Override
+ public boolean moveToPrevious() {
+ return move(-1);
+ }
+
+ @Override
+ public boolean move(int offset) {
+ return moveToPosition(mPos + offset);
+ }
+
+ @Override
+ public boolean moveToPosition(int position) {
+ if (position >= mCount) {
+ if (VERBOSE) Log.v(TAG, "Invalid Positon: " + position + " >= count: " + mCount
+ + ". Moving to last record.");
+ mPos = mCount;
+ super.moveToPosition(mOffset + mPos); // move into "after last" state.
+ return false;
+ }
+
+ // Make sure position isn't before the beginning of the cursor
+ if (position < 0) {
+ if (VERBOSE) Log.v(TAG, "Ignoring invalid move to position: " + position);
+ mPos = -1;
+ super.moveToPosition(mPos);
+ return false;
+ }
+
+ if (position == mPos) {
+ if (VERBOSE) Log.v(TAG, "Ignoring no-op move to position: " + position);
+ return true;
+ }
+
+ int delegatePosition = position + mOffset;
+ if (VERBOSE) Log.v(TAG, "Moving delegate cursor to position: " + delegatePosition);
+ if (super.moveToPosition(delegatePosition)) {
+ mPos = position;
+ return true;
+ } else {
+ mPos = -1;
+ super.moveToPosition(-1);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
+
+ /**
+ * Wraps the cursor such that it will honor paging args (if present), AND if the cursor
+ * does not report paging size.
+ *
+ * <p>No-op if cursor already contains paging or is less than specified page size.
+ */
+ public static Cursor wrap(Cursor cursor, @Nullable Bundle queryArgs) {
+
+ boolean hasPagingArgs =
+ queryArgs != null
+ && (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET)
+ || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT));
+
+ if (!hasPagingArgs) {
+ if (VERBOSE) Log.d(TAG, "No-wrap: No paging args in request.");
+ return cursor;
+ }
+
+ if (hasPagedResponseDetails(cursor.getExtras())) {
+ if (VERBOSE) Log.d(TAG, "No-wrap. Cursor has paging details.");
+ return cursor;
+ }
+
+ return new PageViewCursor(
+ cursor,
+ queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0),
+ queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE));
+ }
+
+ /**
+ * @return true if the extras contains information indicating the associated
+ * cursor is paged.
+ */
+ private static boolean hasPagedResponseDetails(@Nullable Bundle extras) {
+ if (extras != null && extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) {
+ return true;
+ }
+
+ String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
+ if (honoredArgs != null && (
+ ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)
+ || ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 6fdc739..1b1b958 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -23,35 +23,24 @@
public final class ConnectivityMetricsEvent implements Parcelable {
/** The time when this event was collected, as returned by System.currentTimeMillis(). */
- final public long timestamp;
-
- /** The subsystem that generated the event. One of the COMPONENT_TAG_xxx constants. */
- final public int componentTag;
-
- /** The subsystem-specific event ID. */
- final public int eventTag;
+ public long timestamp;
/** Opaque event-specific data. */
- final public Parcelable data;
+ public Parcelable data;
- public ConnectivityMetricsEvent(long timestamp, int componentTag,
- int eventTag, Parcelable data) {
- this.timestamp = timestamp;
- this.componentTag = componentTag;
- this.eventTag = eventTag;
- this.data = data;
+ public ConnectivityMetricsEvent() {
+ }
+
+ public ConnectivityMetricsEvent(Parcel in) {
+ timestamp = in.readLong();
+ data = in.readParcelable(null);
}
/** Implement the Parcelable interface */
public static final Parcelable.Creator<ConnectivityMetricsEvent> CREATOR
= new Parcelable.Creator<ConnectivityMetricsEvent> (){
public ConnectivityMetricsEvent createFromParcel(Parcel source) {
- final long timestamp = source.readLong();
- final int componentTag = source.readInt();
- final int eventTag = source.readInt();
- final Parcelable data = source.readParcelable(null);
- return new ConnectivityMetricsEvent(timestamp, componentTag,
- eventTag, data);
+ return new ConnectivityMetricsEvent(source);
}
public ConnectivityMetricsEvent[] newArray(int size) {
@@ -59,7 +48,6 @@
}
};
- /** Implement the Parcelable interface */
@Override
public int describeContents() {
return 0;
@@ -68,13 +56,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(timestamp);
- dest.writeInt(componentTag);
- dest.writeInt(eventTag);
dest.writeParcelable(data, 0);
}
+ @Override
public String toString() {
- return String.format("ConnectivityMetricsEvent(%tT.%tL, %d, %d): %s",
- timestamp, timestamp, componentTag, eventTag, data);
+ return String.format("ConnectivityMetricsEvent(%tT.%tL): %s", timestamp, timestamp, data);
}
}
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index 173e5fd..aaad1fa 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -74,7 +74,10 @@
}
try {
- int left = mService.logEvent(new ConnectivityMetricsEvent(timestamp, 0, 0, data));
+ ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+ ev.timestamp = timestamp;
+ ev.data = data;
+ int left = mService.logEvent(ev);
return left >= 0;
} catch (RemoteException e) {
Log.e(TAG, "Error logging event", e);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a0d16bc..dac8354 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -44,7 +44,6 @@
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.View;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -8913,11 +8912,15 @@
* ambiguous then the activity should prompt the user for the recipient to send the message
* to.
* <p>
+ * Voice Assistant may provide additional information to messaging app about which account
+ * to use for sending a message by populating {@link #EXTRA_SENDER_ACCOUNT_HASH}.
+ * <p>
* Output: nothing
*
* @see #EXTRA_RECIPIENT_CONTACT_URI
* @see #EXTRA_RECIPIENT_CONTACT_CHAT_ID
* @see #EXTRA_RECIPIENT_CONTACT_NAME
+ * @see #EXTRA_SENDER_ACCOUNT_HASH
* @see #METADATA_ACCOUNT_TYPE
* @see #METADATA_MIMETYPE
*/
@@ -8975,6 +8978,16 @@
"android.provider.extra.RECIPIENT_CONTACT_NAME";
/**
+ * This optional extra specifies the hash of the account that should be used by messaging
+ * app for sending voice message with {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS}. The
+ * value of this extra is a {@code String} and should be the value of {@link
+ * android.accounts.Account#hashCode()} for some account returned by {@link
+ * android.accounts.AccountManager#getAccounts()}.
+ */
+ public static final String EXTRA_SENDER_ACCOUNT_HASH =
+ "android.provider.extra.SENDER_ACCOUNT_HASH";
+
+ /**
* A string associated with an {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} activity
* describing {@link RawContacts#ACCOUNT_TYPE} for the corresponding Contacts Provider
* implementation.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8a2a14c..391ee83 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5605,10 +5605,10 @@
"accessibility_web_content_key_bindings";
/**
- * Setting that specifies whether the display magnification is enabled.
- * Display magnifications allows the user to zoom in the display content
- * and is targeted to low vision users. The current magnification scale
- * is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
+ * Setting that specifies whether the display magnification is enabled via a system-wide
+ * triple tap gesture. Display magnifications allows the user to zoom in the display content
+ * and is targeted to low vision users. The current magnification scale is controlled by
+ * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
*
* @hide
*/
@@ -5616,11 +5616,23 @@
"accessibility_display_magnification_enabled";
/**
+ * Setting that specifies whether the display magnification is enabled via a shortcut
+ * affordance within the system's navigation area. Display magnifications allows the user to
+ * zoom in the display content and is targeted to low vision users. The current
+ * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
+ "accessibility_display_magnification_navbar_enabled";
+
+ /**
* Setting that specifies what the display magnification scale is.
* Display magnifications allows the user to zoom in the display
* content and is targeted to low vision users. Whether a display
* magnification is performed is controlled by
- * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED}
+ * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} and
+ * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED}
*
* @hide
*/
@@ -6950,6 +6962,7 @@
ACCESSIBILITY_DISPLAY_DALTONIZER,
ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
ACCESSIBILITY_SCRIPT_INJECTION,
ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a541a4c..6dbc09c 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -168,6 +168,14 @@
public abstract void setMagnificationSpec(MagnificationSpec spec);
/**
+ * Set by the accessibility framework to indicate whether the magnifiable regions of the display
+ * should be shown.
+ *
+ * @param show {@code true} to show magnifiable region bounds, {@code false} to hide
+ */
+ public abstract void setForceShowMagnifiableBounds(boolean show);
+
+ /**
* Obtains the magnification regions.
*
* @param magnificationRegion the current magnification region
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index c95a1fb..06ac869 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -121,8 +121,8 @@
.classifyText(text.toString(), startIndex, endIndex);
if (results.length > 0) {
// TODO: Added this log for debug only. Remove before release.
- Log.d(LOG_TAG,
- String.format("Classification type: %s", results[0].mCollection));
+ Log.d(LOG_TAG, String.format(
+ "Classification type: %s", getHighestScoringType(results)));
return createClassificationResult(results, classified);
}
}
@@ -188,7 +188,7 @@
builder.setEntityType(classifications[i].mCollection, classifications[i].mScore);
}
- final String type = classifications[0].mCollection;
+ final String type = getHighestScoringType(classifications);
final Intent intent = IntentFactory.create(mContext, type, text.toString());
final PackageManager pm;
final ResolveInfo resolveInfo;
@@ -226,6 +226,23 @@
return builder.build();
}
+ private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) {
+ if (types.length < 1) {
+ return "";
+ }
+
+ String type = types[0].mCollection;
+ float highestScore = types[0].mScore;
+ final int size = types.length;
+ for (int i = 1; i < size; i++) {
+ if (types[i].mScore > highestScore) {
+ type = types[i].mCollection;
+ highestScore = types[i].mScore;
+ }
+ }
+ return type;
+ }
+
/**
* @throws IllegalArgumentException if text is null; startIndex is negative;
* endIndex is greater than text.length() or is not greater than startIndex
@@ -265,7 +282,7 @@
final SmartSelection.ClassificationResult[] results =
smartSelection.classifyText(text, selectionStart, selectionEnd);
if (results.length > 0) {
- final String type = results[0].mCollection;
+ final String type = getHighestScoringType(results);
if (matches(type, linkMask)) {
final Intent intent = IntentFactory.create(
context, type, text.substring(selectionStart, selectionEnd));
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index a6a9db4..59881b5 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -132,7 +132,7 @@
private CharSequence mDescFormat;
- private boolean mRegistered;
+ private boolean mAttached;
private Calendar mTime;
private String mTimeZone;
@@ -252,7 +252,7 @@
}
createTime(mTimeZone);
- // Wait until registering for events to handle the ticker
+ // Wait until onAttachedToWindow() to handle the ticker
chooseFormat(false);
}
@@ -503,9 +503,12 @@
boolean hadSeconds = mHasSeconds;
mHasSeconds = DateFormat.hasSeconds(mFormat);
- if (handleTicker && mRegistered && hadSeconds != mHasSeconds) {
- if (hadSeconds) getHandler().removeCallbacks(mTicker);
- else mTicker.run();
+ if (handleTicker && mAttached && hadSeconds != mHasSeconds) {
+ if (hadSeconds) {
+ getHandler().removeCallbacks(mTicker);
+ } else if (getVisibility() == VISIBLE) {
+ mTicker.run();
+ }
}
}
@@ -517,27 +520,50 @@
}
@Override
- public void onVisibilityAggregated(boolean isVisible) {
- if (!mRegistered && isVisible) {
- mRegistered = true;
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (!mAttached) {
+ mAttached = true;
registerReceiver();
registerObserver();
createTime(mTimeZone);
- if (mHasSeconds) {
- mTicker.run();
- } else {
- onTimeChanged();
+ if (getVisibility() == VISIBLE) {
+ if (mHasSeconds) {
+ mTicker.run();
+ } else {
+ onTimeChanged();
+ }
}
- } else if (mRegistered && !isVisible) {
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mAttached) {
unregisterReceiver();
unregisterObserver();
getHandler().removeCallbacks(mTicker);
- mRegistered = false;
+ mAttached = false;
+ }
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ if (mAttached) {
+ if (isVisible && mHasSeconds) {
+ mTicker.run();
+ } else {
+ getHandler().removeCallbacks(mTicker);
+ }
+ onTimeChanged();
}
}
@@ -560,7 +586,7 @@
}
private void registerObserver() {
- if (mRegistered) {
+ if (mAttached) {
if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(getHandler());
}
@@ -587,9 +613,11 @@
}
private void onTimeChanged() {
- mTime.setTimeInMillis(System.currentTimeMillis());
- setText(DateFormat.format(mFormat, mTime));
- setContentDescription(DateFormat.format(mDescFormat, mTime));
+ if (getVisibility() == VISIBLE) {
+ mTime.setTimeInMillis(System.currentTimeMillis());
+ setText(DateFormat.format(mFormat, mTime));
+ setContentDescription(DateFormat.format(mDescFormat, mTime));
+ }
}
/** @hide */
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index ae31873..95d714f 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -321,7 +321,7 @@
stackBounds.top));
boundsOut.set(stackBounds);
if (mIsMinimized) {
- boundsOut.offsetTo(boundedLeft, boundsOut.top);
+ boundsOut.offsetTo(boundedLeft, boundedTop);
return;
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 79b0cd1..818cc2c 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1163,21 +1163,21 @@
isLastItem && menuItemButtonWidth <= availableWidth - extraPadding;
if (canFitWithOverflow || canFitNoOverflow) {
if (isNewGroup) {
- final View border = createBorder(mContext);
- final int borderWidth = border.getLayoutParams().width;
+ final View divider = createDivider(mContext);
+ final int dividerWidth = divider.getLayoutParams().width;
// Add extra padding to the end of the previous button.
// Half of the extra padding (less borderWidth) goes to the previous button.
View previousButton = mMainPanel.getChildAt(mMainPanel.getChildCount() - 1);
final int prevPaddingEnd = previousButton.getPaddingEnd()
- + extraPadding / 2 - borderWidth;
+ + extraPadding / 2 - dividerWidth;
previousButton.setPaddingRelative(
previousButton.getPaddingStart(),
previousButton.getPaddingTop(),
prevPaddingEnd,
previousButton.getPaddingBottom());
final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams();
- prevParams.width += extraPadding / 2 - borderWidth;
+ prevParams.width += extraPadding / 2 - dividerWidth;
previousButton.setLayoutParams(prevParams);
// Add extra padding to the start of this button.
@@ -1190,8 +1190,8 @@
menuItemButton.getPaddingEnd(),
menuItemButton.getPaddingBottom());
- // Include a border.
- mMainPanel.addView(border);
+ // Include a divider.
+ mMainPanel.addView(divider);
}
setButtonTagAndClickListener(menuItemButton, menuItem);
@@ -1670,21 +1670,28 @@
return popupWindow;
}
- private static View createBorder(Context context) {
+ private static View createDivider(Context context) {
// TODO: Inflate this instead.
- View border = new View(context);
+ View divider = new View(context);
+
int _1dp = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
_1dp, ViewGroup.LayoutParams.MATCH_PARENT);
params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10);
- border.setLayoutParams(params);
- border.setBackgroundColor(Color.parseColor("#9E9E9E"));
- border.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- border.setEnabled(false);
- border.setFocusable(false);
- border.setContentDescription(null);
- return border;
+ divider.setLayoutParams(params);
+
+ TypedArray a = context.obtainStyledAttributes(
+ new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor });
+ divider.setBackgroundColor(a.getColor(0, 0));
+ a.recycle();
+
+ divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ divider.setEnabled(false);
+ divider.setFocusable(false);
+ divider.setContentDescription(null);
+
+ return divider;
}
/**
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index f852194..a8d6830 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -453,7 +453,8 @@
dst = dstBitmap.getAddr(x, y);
SkColorSpace* colorSpace = dstBitmap.colorSpace();
- if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ if (dstBitmap.colorType() == kRGBA_F16_SkColorType ||
+ GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
// now copy/convert each scanline
for (int y = 0; y < height; y++) {
proc(dst, src, width, x, y);
@@ -1267,7 +1268,8 @@
proc(dst, src, 1, bitmap.getColorTable());
SkColorSpace* colorSpace = bitmap.colorSpace();
- if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ if (bitmap.colorType() != kRGBA_F16_SkColorType &&
+ !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
auto sRGB = SkColorSpace::MakeSRGB();
auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0],
@@ -1299,7 +1301,8 @@
SkColor* d = (SkColor*)dst + offset;
SkColorSpace* colorSpace = bitmap.colorSpace();
- if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ if (bitmap.colorType() == kRGBA_F16_SkColorType ||
+ GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
while (--height >= 0) {
proc(d, src, width, ctable);
d += stride;
@@ -1342,7 +1345,8 @@
}
SkColorSpace* colorSpace = bitmap.colorSpace();
- if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ if (bitmap.colorType() != kRGBA_F16_SkColorType &&
+ !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
auto sRGB = SkColorSpace::MakeSRGB();
auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color,
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 1846237..fa25a8f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -239,30 +239,38 @@
return result;
}
- static jint doTextRunCursor(JNIEnv *env, Paint* paint, const jchar *text, jint start,
- jint count, jint flags, jint offset, jint opt) {
+ static jint doTextRunCursor(JNIEnv *env, Paint* paint, Typeface* typeface, const jchar *text,
+ jint start, jint count, jint dir, jint offset, jint opt) {
minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt);
- size_t result = minikin::GraphemeBreak::getTextRunCursor(text, start, count, offset,
- moveOpt);
+ int bidiFlags = dir == 1 ? minikin::kBidi_Force_RTL : minikin::kBidi_Force_LTR;
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
+ advancesArray.get());
+ size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
+ start, count, offset, moveOpt);
return static_cast<jint>(result);
}
- static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
- jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
+ static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jlong typefaceHandle, jcharArray text, jint contextStart, jint contextCount, jint dir,
+ jint offset, jint cursorOpt) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, nullptr);
- jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
- offset, cursorOpt);
+ jint result = doTextRunCursor(env, paint, typeface, textArray,
+ contextStart, contextCount, dir, offset, cursorOpt);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
- static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
- jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
+ static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jlong typefaceHandle, jstring text, jint contextStart, jint contextEnd, jint dir,
+ jint offset, jint cursorOpt) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, nullptr);
- jint result = doTextRunCursor(env, paint, textArray, contextStart,
- contextEnd - contextStart, dir, offset, cursorOpt);
+ jint result = doTextRunCursor(env, paint, typeface, textArray,
+ contextStart, contextEnd - contextStart, dir, offset, cursorOpt);
env->ReleaseStringChars(text, textArray);
return result;
}
@@ -983,8 +991,8 @@
{"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
(void*) PaintGlue::getTextAdvances__StringIIIII_FI},
- {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
- {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
+ {"nGetTextRunCursor", "(JJ[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+ {"nGetTextRunCursor", "(JJLjava/lang/String;IIIII)I",
(void*) PaintGlue::getTextRunCursor__String},
{"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
{"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c547ae5..4432e3c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -576,6 +576,7 @@
<attr name="floatingToolbarItemBackgroundDrawable" format="reference" />
<attr name="floatingToolbarOpenDrawable" format="reference" />
<attr name="floatingToolbarPopupBackgroundDrawable" format="reference" />
+ <attr name="floatingToolbarDividerColor" format="reference" />
<!-- ============ -->
<!-- Alert Dialog styles -->
@@ -8379,7 +8380,9 @@
<!-- Component name of an activity that allows the user to set up this service. -->
<attr name="setupActivity" format="string" />
<!-- Component name of an activity that allows the user to modify the settings for this
- service. -->
+ service.
+ {@deprecated This value is deprecated and not used by the framework starting from API
+ level 26. Use setupActivity instead.} -->
<attr name="settingsActivity" />
<!-- Attribute whether the TV input service can record programs. This value can be changed
at runtime by calling
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 6015ed5..f9fd57c 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -194,4 +194,8 @@
<color name="tooltip_background_dark">#e6616161</color>
<color name="tooltip_background_light">#e6FFFFFF</color>
+
+ <!-- FloatingToolbar -->
+ <color name="floating_popup_divider_dark">#2F2F2F</color>
+ <color name="floating_popup_divider_light">#E9E9E9</color>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0c318cf..26d71c3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2425,6 +2425,7 @@
<java-symbol type="drawable" name="ft_avd_toarrow" />
<java-symbol type="drawable" name="ft_avd_toarrow_animation" />
<java-symbol type="drawable" name="ft_avd_tooverflow_animation" />
+ <java-symbol type="attr" name="floatingToolbarDividerColor" />
<java-symbol type="string" name="date_picker_prev_month_button" />
<java-symbol type="string" name="date_picker_next_month_button" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index d100c63..a661b07 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -398,6 +398,7 @@
<item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item>
<item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item>
<item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item>
+ <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item>
<!-- SearchView attributes -->
<item name="searchViewStyle">@style/Widget.Holo.SearchView</item>
@@ -559,6 +560,7 @@
<item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item>
<item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item>
<item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item>
+ <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item>
<!-- Tooltip popup colors -->
<item name="tooltipForegroundColor">@color/bright_foreground_dark</item>
diff --git a/core/tests/coretests/src/android/database/PageViewCursorTest.java b/core/tests/coretests/src/android/database/PageViewCursorTest.java
new file mode 100644
index 0000000..0be89d5
--- /dev/null
+++ b/core/tests/coretests/src/android/database/PageViewCursorTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.database;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.MathUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class PageViewCursorTest {
+
+ private static final int ITEM_COUNT = 20;
+
+ private static final String NAME_COLUMN = "name";
+ private static final String NUM_COLUMN = "num";
+
+ private static final String[] COLUMNS = new String[]{
+ NAME_COLUMN,
+ NUM_COLUMN
+ };
+
+ private static final String[] NAMES = new String[] {
+ "000",
+ "111",
+ "222",
+ "333",
+ "444",
+ "555",
+ "666",
+ "777",
+ "888",
+ "999",
+ "aaa",
+ "bbb",
+ "ccc",
+ "ddd",
+ "eee",
+ "fff",
+ "ggg",
+ "hhh",
+ "iii",
+ "jjj"
+ };
+
+ private MatrixCursor mDelegate;
+ private PageViewCursor mCursor;
+
+ @Before
+ public void setUp() {
+ Random rand = new Random();
+
+ mDelegate = new MatrixCursor(COLUMNS);
+ for (int i = 0; i < ITEM_COUNT; i++) {
+ MatrixCursor.RowBuilder row = mDelegate.newRow();
+ row.add(NAME_COLUMN, NAMES[i]);
+ row.add(NUM_COLUMN, rand.nextInt());
+ }
+
+ mCursor = new PageViewCursor(mDelegate, 10, 5);
+ }
+
+ @Test
+ public void testPage_Size() {
+ assertEquals(5, mCursor.getCount());
+ }
+
+ @Test
+ public void testPage_TotalSize() {
+ assertEquals(ITEM_COUNT, mCursor.getExtras().getInt(ContentResolver.EXTRA_TOTAL_SIZE));
+ }
+
+ @Test
+ public void testPage_OffsetExceedsCursorCount_EffectivelyEmptyCursor() {
+ mCursor = new PageViewCursor(mDelegate, ITEM_COUNT * 2, 5);
+ assertEquals(0, mCursor.getCount());
+ }
+
+ @Test
+ public void testMoveToPosition() {
+ assertTrue(mCursor.moveToPosition(0));
+ assertEquals(NAMES[10], mCursor.getString(0));
+ assertTrue(mCursor.moveToPosition(1));
+ assertEquals(NAMES[11], mCursor.getString(0));
+ assertTrue(mCursor.moveToPosition(4));
+ assertEquals(NAMES[14], mCursor.getString(0));
+
+ // and then back down again for good measure.
+ assertTrue(mCursor.moveToPosition(1));
+ assertEquals(NAMES[11], mCursor.getString(0));
+ assertTrue(mCursor.moveToPosition(0));
+ assertEquals(NAMES[10], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToPosition_MoveToSamePosition_NoOp() {
+ assertTrue(mCursor.moveToPosition(1));
+ assertEquals(NAMES[11], mCursor.getString(0));
+ assertTrue(mCursor.moveToPosition(1));
+ assertEquals(NAMES[11], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToPosition_PositionOutOfBounds_MovesToBeforeFirst() {
+ assertTrue(mCursor.moveToPosition(0));
+ assertEquals(NAMES[10], mCursor.getString(0));
+
+ // move before
+ assertFalse(mCursor.moveToPosition(-12));
+ assertTrue(mCursor.isBeforeFirst());
+ }
+
+ @Test
+ public void testMoveToPosition_PositionOutOfBounds_MovesToAfterLast() {
+ assertTrue(mCursor.moveToPosition(0));
+ assertEquals(NAMES[10], mCursor.getString(0));
+
+ assertFalse(mCursor.moveToPosition(222));
+ assertTrue(mCursor.isAfterLast());
+ }
+
+ @Test
+ public void testPosition() {
+ assertEquals(-1, mCursor.getPosition());
+ }
+
+ @Test
+ public void testIsBeforeFirst() {
+ assertTrue(mCursor.isBeforeFirst());
+ mCursor.moveToFirst();
+ assertFalse(mCursor.isBeforeFirst());
+ }
+
+ @Test
+ public void testCount_ZeroForEmptyCursor() {
+ mCursor = new PageViewCursor(mDelegate, 0, 0);
+ assertEquals(0, mCursor.getCount());
+ }
+
+ @Test
+ public void testIsBeforeFirst_TrueForEmptyCursor() {
+ mCursor = new PageViewCursor(mDelegate, 0, 0);
+ assertTrue(mCursor.isBeforeFirst());
+ }
+
+ @Test
+ public void testIsAfterLast() {
+ assertFalse(mCursor.isAfterLast());
+ mCursor.moveToLast();
+ mCursor.moveToNext();
+ assertTrue(mCursor.isAfterLast());
+ }
+
+ @Test
+ public void testIsAfterLast_TrueForEmptyCursor() {
+ mCursor = new PageViewCursor(mDelegate, 0, 0);
+ assertTrue(mCursor.isAfterLast());
+ }
+
+ @Test
+ public void testIsFirst() {
+ assertFalse(mCursor.isFirst());
+ mCursor.moveToFirst();
+ assertTrue(mCursor.isFirst());
+ }
+
+ @Test
+ public void testIsLast() {
+ assertFalse(mCursor.isLast());
+ mCursor.moveToLast();
+ assertTrue(mCursor.isLast());
+ }
+
+ @Test
+ public void testMove() {
+ // note that initial position is -1, so moving
+ // 2 will only put as at 1.
+ mCursor.move(2);
+ assertEquals(NAMES[11], mCursor.getString(0));
+ mCursor.move(-1);
+ assertEquals(NAMES[10], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToFist() {
+ mCursor.moveToPosition(3);
+ mCursor.moveToFirst();
+ assertEquals(NAMES[10], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToLast() {
+ mCursor.moveToLast();
+ assertEquals(NAMES[14], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToNext() {
+ // default position is -1, so next is 0.
+ mCursor.moveToNext();
+ assertEquals(NAMES[10], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToNext_AfterLastReturnsFalse() {
+ mCursor.moveToLast();
+ assertFalse(mCursor.moveToNext());
+ }
+
+ @Test
+ public void testMoveToPrevious() {
+ mCursor.moveToPosition(3);
+ mCursor.moveToPrevious();
+ assertEquals(NAMES[12], mCursor.getString(0));
+ }
+
+ @Test
+ public void testMoveToPrevious_BeforeFirstReturnsFalse() {
+ assertFalse(mCursor.moveToPrevious());
+ }
+
+ @Test
+ public void testWindow_ReadPastEnd() {
+ assertFalse(mCursor.moveToPosition(10));
+ }
+
+ @Test
+ public void testOffset_LimitOutOfBounds() {
+ mCursor = new PageViewCursor(mDelegate, 5, 100);
+ assertEquals(15, mCursor.getCount());
+ }
+
+ @Test
+ public void testPagingMarker() {
+ mCursor = new PageViewCursor(mDelegate, 5, 100);
+ assertTrue(mCursor.getExtras().getBoolean(PageViewCursor.EXTRA_AUTO_PAGED));
+ }
+
+ @Test
+ public void testWrap() {
+ Bundle queryArgs = new Bundle();
+ queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+ queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+ Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+ assertTrue(wrapped instanceof PageViewCursor);
+ assertEquals(5, wrapped.getCount());
+ }
+
+ @Test
+ public void testWrap_NoOpWithoutPagingArgs() {
+ Cursor wrapped = PageViewCursor.wrap(mDelegate, Bundle.EMPTY);
+ assertTrue(mDelegate == wrapped);
+ }
+
+ @Test
+ public void testWrap_NoOpCursorsWithExistingPaging_ByTotalSize() {
+ Bundle extras = new Bundle();
+ extras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, 5);
+ mDelegate.setExtras(extras);
+
+ Bundle queryArgs = new Bundle();
+ queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+ queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+ Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+ assertTrue(mDelegate == wrapped);
+ }
+
+ @Test
+ public void testWrap_NoOpCursorsWithExistingPaging_ByHonoredArgs() {
+ Bundle extras = new Bundle();
+ extras.putStringArray(
+ ContentResolver.EXTRA_HONORED_ARGS,
+ new String[] {
+ ContentResolver.QUERY_ARG_OFFSET,
+ ContentResolver.QUERY_ARG_LIMIT
+ });
+ mDelegate.setExtras(extras);
+
+ Bundle queryArgs = new Bundle();
+ queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+ queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+ Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+ assertTrue(mDelegate == wrapped);
+ }
+
+ private void assertStringAt(int row, int column, String expected) {
+ mCursor.moveToPosition(row);
+ assertEquals(expected, mCursor.getString(column));
+ }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 3d5ba79..ed587bb 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -791,12 +791,12 @@
int neww = width;
int newh = height;
- Canvas canvas = new Canvas();
Bitmap bitmap;
Paint paint;
Rect srcR = new Rect(x, y, x + width, y + height);
RectF dstR = new RectF(0, 0, width, height);
+ RectF deviceR = new RectF();
Config newConfig = Config.ARGB_8888;
final Config config = source.getConfig();
@@ -827,7 +827,6 @@
} else {
final boolean transformed = !m.rectStaysRect();
- RectF deviceR = new RectF();
m.mapRect(deviceR, dstR);
neww = Math.round(deviceR.width());
@@ -841,9 +840,6 @@
}
bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
- canvas.translate(-deviceR.left, -deviceR.top);
- canvas.concat(m);
-
paint = new Paint();
paint.setFilterBitmap(filter);
if (transformed) {
@@ -857,7 +853,9 @@
bitmap.setHasAlpha(source.hasAlpha());
bitmap.setPremultiplied(source.mRequestPremultiplied);
- canvas.setBitmap(bitmap);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.translate(-deviceR.left, -deviceR.top);
+ canvas.concat(m);
canvas.drawBitmap(source, srcR, dstR, paint);
canvas.setBitmap(null);
if (isHardware) {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b1d51ec..7ca4615 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2294,7 +2294,7 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text,
+ return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
contextStart, contextLength, dir, offset, cursorOpt);
}
@@ -2380,7 +2380,7 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text,
+ return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
contextStart, contextEnd, dir, offset, cursorOpt);
}
@@ -2686,9 +2686,9 @@
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
String text, int start, int end, int contextStart, int contextEnd,
int bidiFlags, float[] advances, int advancesIndex);
- private native int nGetTextRunCursor(long paintPtr, char[] text,
+ private native int nGetTextRunCursor(long paintPtr, long typefacePtr, char[] text,
int contextStart, int contextLength, int dir, int offset, int cursorOpt);
- private native int nGetTextRunCursor(long paintPtr, String text,
+ private native int nGetTextRunCursor(long paintPtr, long typefacePtr, String text,
int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, char[] text, int index, int count, float x, float y, long path);
diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk
index 1fec504..636f057 100644
--- a/libs/common_time/Android.mk
+++ b/libs/common_time/Android.mk
@@ -15,7 +15,8 @@
clock_recovery.cpp \
common_clock.cpp \
main.cpp \
- utils.cpp
+ utils.cpp \
+ LinearTransform.cpp
# Uncomment to enable vesbose logging and debug service.
#TIME_SERVICE_DEBUG=true
diff --git a/libs/common_time/LinearTransform.cpp b/libs/common_time/LinearTransform.cpp
new file mode 100644
index 0000000..6730855
--- /dev/null
+++ b/libs/common_time/LinearTransform.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include "LinearTransform.h"
+#include <assert.h>
+
+
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
+namespace android {
+
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
+
+// Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static bool scale_u64_to_u64(
+ uint64_t val,
+ uint32_t N,
+ uint32_t D,
+ uint64_t* res,
+ bool round_up_not_down) {
+ uint64_t tmp1, tmp2;
+ uint32_t r;
+
+ assert(res);
+ assert(D);
+
+ // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
+ // integer X.
+ // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
+ // integer X.
+ // Let X[A, B] with A <= B denote bits A through B of the integer X.
+ // Let (A | B) denote the concatination of two 32 bit ints, A and B.
+ // IOW X = (A | B) => U32(X) == A && L32(X) == B
+ //
+ // compute M = val * N (a 96 bit int)
+ // ---------------------------------
+ // tmp2 = U32(val) * N (a 64 bit int)
+ // tmp1 = L32(val) * N (a 64 bit int)
+ // which means
+ // M = val * N = (tmp2 << 32) + tmp1
+ tmp2 = (val >> 32) * N;
+ tmp1 = (val & UINT32_MAX) * N;
+
+ // compute M[32, 95]
+ // tmp2 = tmp2 + U32(tmp1)
+ // = (U32(val) * N) + U32(L32(val) * N)
+ // = M[32, 95]
+ tmp2 += tmp1 >> 32;
+
+ // if M[64, 95] >= D, then M/D has bits > 63 set and we have
+ // an overflow.
+ if ((tmp2 >> 32) >= D) {
+ *res = UINT64_MAX;
+ return false;
+ }
+
+ // Divide. Going in we know
+ // tmp2 = M[32, 95]
+ // U32(tmp2) < D
+ r = tmp2 % D;
+ tmp2 /= D;
+
+ // At this point
+ // tmp1 = L32(val) * N
+ // tmp2 = M[32, 95] / D
+ // = (M / D)[32, 95]
+ // r = M[32, 95] % D
+ // U32(tmp2) = 0
+ //
+ // compute tmp1 = (r | M[0, 31])
+ tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
+
+ // Divide again. Keep the remainder around in order to round properly.
+ r = tmp1 % D;
+ tmp1 /= D;
+
+ // At this point
+ // tmp2 = (M / D)[32, 95]
+ // tmp1 = (M / D)[ 0, 31]
+ // r = M % D
+ // U32(tmp1) = 0
+ // U32(tmp2) = 0
+
+ // Pack the result and deal with the round-up case (As well as the
+ // remote possiblility over overflow in such a case).
+ *res = (tmp2 << 32) | tmp1;
+ if (r && round_up_not_down) {
+ ++(*res);
+ if (!(*res)) {
+ *res = UINT64_MAX;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static bool linear_transform_s64_to_s64(
+ int64_t val,
+ int64_t basis1,
+ int32_t N,
+ uint32_t D,
+ bool invert_frac,
+ int64_t basis2,
+ int64_t* out) {
+ uint64_t scaled, res;
+ uint64_t abs_val;
+ bool is_neg;
+
+ if (!out)
+ return false;
+
+ // Compute abs(val - basis_64). Keep track of whether or not this delta
+ // will be negative after the scale opertaion.
+ if (val < basis1) {
+ is_neg = true;
+ abs_val = basis1 - val;
+ } else {
+ is_neg = false;
+ abs_val = val - basis1;
+ }
+
+ if (N < 0)
+ is_neg = !is_neg;
+
+ if (!scale_u64_to_u64(abs_val,
+ invert_frac ? D : ABS(N),
+ invert_frac ? ABS(N) : D,
+ &scaled,
+ is_neg))
+ return false; // overflow/undeflow
+
+ // if scaled is >= 0x8000<etc>, then we are going to overflow or
+ // underflow unless ABS(basis2) is large enough to pull us back into the
+ // non-overflow/underflow region.
+ if (scaled & INT64_MIN) {
+ if (is_neg && (basis2 < 0))
+ return false; // certain underflow
+
+ if (!is_neg && (basis2 >= 0))
+ return false; // certain overflow
+
+ if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
+ return false; // not enough
+
+ // Looks like we are OK
+ *out = (is_neg ? (-scaled) : scaled) + basis2;
+ } else {
+ // Scaled fits within signed bounds, so we just need to check for
+ // over/underflow for two signed integers. Basically, if both scaled
+ // and basis2 have the same sign bit, and the result has a different
+ // sign bit, then we have under/overflow. An easy way to compute this
+ // is
+ // (scaled_signbit XNOR basis_signbit) &&
+ // (scaled_signbit XOR res_signbit)
+ // ==
+ // (scaled_signbit XOR basis_signbit XOR 1) &&
+ // (scaled_signbit XOR res_signbit)
+
+ if (is_neg)
+ scaled = -scaled; // known sanitize failure
+ res = scaled + basis2;
+
+ if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
+ return false;
+
+ *out = res;
+ }
+
+ return true;
+}
+
+bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
+ if (0 == a_to_b_denom)
+ return false;
+
+ return linear_transform_s64_to_s64(a_in,
+ a_zero,
+ a_to_b_numer,
+ a_to_b_denom,
+ false,
+ b_zero,
+ b_out);
+}
+
+bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
+ if (0 == a_to_b_numer)
+ return false;
+
+ return linear_transform_s64_to_s64(b_in,
+ b_zero,
+ a_to_b_numer,
+ a_to_b_denom,
+ true,
+ a_zero,
+ a_out);
+}
+
+template <class T> void LinearTransform::reduce(T* N, T* D) {
+ T a, b;
+ if (!N || !D || !(*D)) {
+ assert(false);
+ return;
+ }
+
+ a = *N;
+ b = *D;
+
+ if (a == 0) {
+ *D = 1;
+ return;
+ }
+
+ // This implements Euclid's method to find GCD.
+ if (a < b) {
+ T tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ while (1) {
+ // a is now the greater of the two.
+ const T remainder = a % b;
+ if (remainder == 0) {
+ *N /= b;
+ *D /= b;
+ return;
+ }
+ // by swapping remainder and b, we are guaranteeing that a is
+ // still the greater of the two upon entrance to the loop.
+ a = b;
+ b = remainder;
+ }
+};
+
+template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
+template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
+void LinearTransform::reduce(int32_t* N, uint32_t* D) {
+ if (N && D && *D) {
+ if (*N < 0) {
+ *N = -(*N);
+ reduce(reinterpret_cast<uint32_t*>(N), D);
+ *N = -(*N);
+ } else {
+ reduce(reinterpret_cast<uint32_t*>(N), D);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/common_time/LinearTransform.h b/libs/common_time/LinearTransform.h
new file mode 100644
index 0000000..bf6ab8e
--- /dev/null
+++ b/libs/common_time/LinearTransform.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LINEAR_TRANSFORM_H
+#define _LINEAR_TRANSFORM_H
+
+#include <stdint.h>
+
+namespace android {
+
+// LinearTransform defines a structure which hold the definition of a
+// transformation from single dimensional coordinate system A into coordinate
+// system B (and back again). Values in A and in B are 64 bit, the linear
+// scale factor is expressed as a rational number using two 32 bit values.
+//
+// Specifically, let
+// f(a) = b
+// F(b) = f^-1(b) = a
+// then
+//
+// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
+//
+// and
+//
+// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
+//
+struct LinearTransform {
+ int64_t a_zero;
+ int64_t b_zero;
+ int32_t a_to_b_numer;
+ uint32_t a_to_b_denom;
+
+ // Transform from A->B
+ // Returns true on success, or false in the case of a singularity or an
+ // overflow.
+ bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
+
+ // Transform from B->A
+ // Returns true on success, or false in the case of a singularity or an
+ // overflow.
+ bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
+
+ // Helpers which will reduce the fraction N/D using Euclid's method.
+ template <class T> static void reduce(T* N, T* D);
+ static void reduce(int32_t* N, uint32_t* D);
+};
+
+
+}
+
+#endif // _LINEAR_TRANSFORM_H
diff --git a/libs/common_time/clock_recovery.h b/libs/common_time/clock_recovery.h
index 278a75e..8066a39 100644
--- a/libs/common_time/clock_recovery.h
+++ b/libs/common_time/clock_recovery.h
@@ -19,9 +19,10 @@
#include <stdint.h>
#include <common_time/ICommonClock.h>
-#include <utils/LinearTransform.h>
#include <utils/threads.h>
+#include "LinearTransform.h"
+
#ifdef TIME_SERVICE_DEBUG
#include "diag_thread.h"
#endif
diff --git a/libs/common_time/common_clock.cpp b/libs/common_time/common_clock.cpp
index ee326e1..aed52f1 100644
--- a/libs/common_time/common_clock.cpp
+++ b/libs/common_time/common_clock.cpp
@@ -23,7 +23,6 @@
#include <stdint.h>
#include <utils/Errors.h>
-#include <utils/LinearTransform.h>
#include "common_clock.h"
diff --git a/libs/common_time/common_clock.h b/libs/common_time/common_clock.h
index b786fdc..5e4e5f5 100644
--- a/libs/common_time/common_clock.h
+++ b/libs/common_time/common_clock.h
@@ -20,9 +20,10 @@
#include <stdint.h>
#include <utils/Errors.h>
-#include <utils/LinearTransform.h>
#include <utils/threads.h>
+#include "LinearTransform.h"
+
namespace android {
class CommonClock {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 39184f1..0906ba5 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -677,7 +677,7 @@
((audioSource > MediaRecorder.getAudioSourceMax()) &&
(audioSource != MediaRecorder.AudioSource.RADIO_TUNER) &&
(audioSource != MediaRecorder.AudioSource.HOTWORD)) ) {
- throw new IllegalArgumentException("Invalid audio source.");
+ throw new IllegalArgumentException("Invalid audio source " + audioSource);
}
mRecordSource = audioSource;
@@ -703,8 +703,8 @@
mAudioFormat = audioFormat;
break;
default:
- throw new IllegalArgumentException("Unsupported sample encoding."
- + " Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
+ throw new IllegalArgumentException("Unsupported sample encoding " + audioFormat
+ + ". Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
}
}
@@ -722,7 +722,8 @@
int frameSizeInBytes = mChannelCount
* (AudioFormat.getBytesPerSample(mAudioFormat));
if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
- throw new IllegalArgumentException("Invalid audio buffer size.");
+ throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
+ + " (frame size " + frameSizeInBytes + ")");
}
mNativeBufferSizeInBytes = audioBufferSize;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d264127..13a22b4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaMetricsSet;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -3186,59 +3187,22 @@
public native final String getName();
/**
- * Returns Analytics/Metrics data about the current content being
+ * Return Metrics data about the current codec instance.
*
- * @return a Bundle containing the set of attributes and values available
- * for the media being handled by this instance of MediaCodec
+ * @return a MediaMetricsSet containing the set of attributes and values
+ * available for the media being handled by this instance of MediaCodec
+ * The attributes are descibed in {@link MediaMetricsSet.MediaCodec}.
*
- * <table style="width: 0%">
- * <thead>
- * <tr>
- * <th>Key</th>
- * <th>Type</th>
- * <th>Description</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td>{@code "codec"}</td>
- * <td>String</td>
- * <td>Identifies the particular codec in use</td>
- * </tr><tr>
- * <td>{@code "mime"}</td>
- * <td>String</td>
- * <td>Mime type of the media being encoded/decoded</td>
- * </tr><tr>
- * <td>{@code "mode"}</td>
- * <td>String</td>
- * <td>"Audio" or "Video"</td>
- * </tr><tr>
- * <td>{@code "secure"}</td>
- * <td>Integer</td>
- * <td>Indicates whether the code is operating on secure content and
- * may also use capabilities in android.media.MediaCrypto</td>
- * </tr><tr>
- * <td>{@code "height"}</td>
- * <td>Integer</td>
- * <td>Height (pixels); valid only when mode=video</td>
- * </tr><tr>
- * <td>{@code "width"}</td>
- * <td>Integer</td>
- * <td>Width (pixels); valid only when mode=video</td>
- * </tr><tr>
- * <td>{@code "rotation"}</td>
- * <td>Integer</td>
- * <td>rotation (degrees) to orient the video onto the target surface;
- * valid only when mode=video. Note there may be additional
- * rotations applied when the surface is mapped to the screen.</td>
- * </tr>
- * </tbody>
- * </table>
- *
- * Additional fields specific to individual codecs will also appear in
+ * Additional vendor-specific fields may also be present in
* the return value.
*/
- public native Bundle getMetrics();
+ public MediaMetricsSet getMetrics() {
+ Bundle bundle = native_getMetrics();
+ MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+ return mSet;
+ }
+
+ private native Bundle native_getMetrics();
/**
* Change a video encoder's target bitrate on the fly. The value is an
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b9e409d..2ed6668 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -25,6 +25,7 @@
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaHTTPService;
+import android.media.MediaMetricsSet;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -651,41 +652,24 @@
public native boolean hasCacheReachedEndOfStream();
/**
- * Returns Analytics/Metrics data about the current media container.
+ * Return Metrics data about the current media container.
*
- * @return the set of keys and values available for the media being
- * handled by this instance of MediaExtractor
+ * @return a MediaMetricsSet containing the set of attributes and values
+ * available for the media container being handled by this instance
+ * of MediaExtractor.
+ * The attributes are descibed in {@link MediaMetricsSet.MediaExtractor}.
*
- * <table style="width: 0%">
- * <thead>
- * <tr>
- * <th>Key</th>
- * <th>Type</th>
- * <th>Description</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td>{@code "fmt"}</td>
- * <td>String</td>
- * <td>The container format (which determines the handler)</td>
- * </tr><tr>
- * <td>{@code "mime"}</td>
- * <td>String</td>
- * <td>Mime type of the container.</td>
- * </tr><tr>
- * <td>{@code "ntrk"}</td>
- * <td>Integer</td>
- * <td>Number of tracks in the container</td>
- * </tr>
- * </tbody>
- * </table>
- *
- * Additional fields specific to individual codecs will also appear in
+ * Additional vendor-specific fields may also be present in
* the return value.
*/
- public native Bundle getMetrics();
+ public MediaMetricsSet getMetrics() {
+ Bundle bundle = native_getMetrics();
+ MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+ return mSet;
+ }
+
+ private native Bundle native_getMetrics();
private static native final void native_init();
private native final void native_setup();
diff --git a/media/java/android/media/MediaMetricsSet.java b/media/java/android/media/MediaMetricsSet.java
new file mode 100644
index 0000000..5ecbee2
--- /dev/null
+++ b/media/java/android/media/MediaMetricsSet.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.Runnable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.net.HttpCookie;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.UUID;
+import java.util.Vector;
+
+
+/**
+ * MediaMetricsSet contains the results returned by the getMetrics()
+ * methods defined in other Media classes such as
+ * {@link MediaCodec}, {@link MediaExtractor}, {@link MediaPlayer},
+ * and {@link MediaRecorder}.
+ *
+ * MediaMetricsSet behaves similarly to a {@link Bundle}. It contains
+ * a set of keys and values.
+ * Methods such as {@link #getInt} and {@link #getString} are provided
+ * to extract values of the corresponding types.
+ * The {@link #keySet} method can be used to discover all of the keys
+ * that are present in the particular instance.
+ *
+ */
+public final class MediaMetricsSet
+{
+
+ /**
+ * This MediaCodec class holds the constants defining keys related to
+ * the metrics for a MediaCodec.
+ */
+ public final static class MediaCodec
+ {
+ private MediaCodec() {}
+
+ /**
+ * Key to extract the codec being used
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_CODEC = "android.media.mediacodec.codec";
+
+ /**
+ * Key to extract the MIME type
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_MIME = "android.media.mediacodec.mime";
+
+ /**
+ * Key to extract what the codec mode
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String. Values will be one of the constants
+ * MODE_AUDIO or MODE_VIDEO.
+ */
+ public static final String KEY_MODE = "android.media.mediacodec.mode";
+
+ /**
+ * The value returned for the key {@link #KEY_MODE} when the
+ * codec is a audio codec.
+ */
+ public static final String MODE_AUDIO = "audio";
+
+ /**
+ * The value returned for the key {@link #KEY_MODE} when the
+ * codec is a video codec.
+ */
+ public static final String MODE_VIDEO = "video";
+
+ /**
+ * Key to extract the flag indicating whether the codec is running
+ * as an encoder or decoder from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ * A 0 indicates decoder; 1 indicates encoder.
+ */
+ public static final String KEY_ENCODER = "android.media.mediacodec.encoder";
+
+ /**
+ * Key to extract the flag indicating whether the codec is running
+ * in secure (DRM) mode from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_SECURE = "android.media.mediacodec.secure";
+
+ /**
+ * Key to extract the width (in pixels) of the video track
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_WIDTH = "android.media.mediacodec.width";
+
+ /**
+ * Key to extract the height (in pixels) of the video track
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_HEIGHT = "android.media.mediacodec.height";
+
+ /**
+ * Key to extract the rotation (in degrees) to properly orient the video
+ * from the {@link MediaCodec#getMetrics} return.
+ * The value is a integer.
+ */
+ public static final String KEY_ROTATION = "android.media.mediacodec.rotation";
+
+ }
+
+ /**
+ * This class holds the constants defining keys related to
+ * the metrics for a MediaExtractor.
+ */
+ public final static class MediaExtractor
+ {
+ private MediaExtractor() {}
+
+ /**
+ * Key to extract the container format
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_FORMAT = "android.media.mediaextractor.fmt";
+
+ /**
+ * Key to extract the container MIME type
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_MIME = "android.media.mediaextractor.mime";
+
+ /**
+ * Key to extract the number of tracks in the container
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+
+ }
+
+ /**
+ * This class holds the constants defining keys related to
+ * the metrics for a MediaPlayer.
+ */
+ public final static class MediaPlayer
+ {
+ private MediaPlayer() {}
+
+ /**
+ * Key to extract the MIME type of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+
+ /**
+ * Key to extract the codec being used to decode the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+
+ /**
+ * Key to extract the width (in pixels) of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_WIDTH = "android.media.mediaplayer.width";
+
+ /**
+ * Key to extract the height (in pixels) of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_HEIGHT = "android.media.mediaplayer.height";
+
+ /**
+ * Key to extract the count of video frames played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_FRAMES = "android.media.mediaplayer.frames";
+
+ /**
+ * Key to extract the count of video frames dropped
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+
+ /**
+ * Key to extract the MIME type of the audio track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+
+ /**
+ * Key to extract the codec being used to decode the audio track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+
+ /**
+ * Key to extract the duration (in milliseconds) of the
+ * media being played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a long.
+ */
+ public static final String KEY_DURATION = "android.media.mediaplayer.durationMs";
+
+ /**
+ * Key to extract the playing time (in milliseconds) of the
+ * media being played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a long.
+ */
+ public static final String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+
+ /**
+ * Key to extract the count of errors encountered while
+ * playing the media
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_ERRORS = "android.media.mediaplayer.err";
+
+ /**
+ * Key to extract an (optional) error code detected while
+ * playing the media
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+
+ }
+
+ /**
+ * This class holds the constants defining keys related to
+ * the metrics for a MediaRecorder.
+ */
+ public final static class MediaRecorder
+ {
+ private MediaRecorder() {}
+
+ /**
+ * Key to extract the audio bitrate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+
+ /**
+ * Key to extract the number of audio channels
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+
+ /**
+ * Key to extract the audio samplerate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+
+ /**
+ * Key to extract the audio timescale
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+
+ /**
+ * Key to extract the video capture frame rate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is a double.
+ */
+ public static final String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+
+ /**
+ * Key to extract the video capture framerate enable value
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+
+ /**
+ * Key to extract the intended playback frame rate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+
+ /**
+ * Key to extract the height (in pixels) of the captured video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_HEIGHT = "android.media.mediarecorder.height";
+
+ /**
+ * Key to extract the recorded movies time units
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ * A value of 1000 indicates that the movie's timing is in milliseconds.
+ */
+ public static final String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+
+ /**
+ * Key to extract the rotation (in degrees) to properly orient the video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_ROTATION = "android.media.mediarecorder.rotation";
+
+ /**
+ * Key to extract the video bitrate from being used
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+
+ /**
+ * Key to extract the value for how often video iframes are generated
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+
+ /**
+ * Key to extract the video encoding level
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+
+ /**
+ * Key to extract the video encoding profile
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+
+ /**
+ * Key to extract the recorded video time units
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ * A value of 1000 indicates that the video's timing is in milliseconds.
+ */
+ public static final String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+
+ /**
+ * Key to extract the width (in pixels) of the captured video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String KEY_WIDTH = "android.media.mediarecorder.width";
+
+ }
+
+ /*
+ * Methods that we want
+ */
+
+ private Bundle mBundle;
+
+ MediaMetricsSet(Bundle bundle) {
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the number of mappings contained in this Bundle.
+ *
+ * @return the number of mappings as an int.
+ */
+ public int size() {
+ return mBundle.size();
+ }
+
+ /**
+ * Returns true if the mapping of this MediaMetricsSet is empty,
+ * false otherwise.
+ */
+ public boolean isEmpty() {
+ return mBundle.isEmpty();
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @param defaultValue Value to return if key does not exist
+ * @return a double value
+ */
+ public double getDouble(String key, double defaultValue) {
+ return mBundle.getDouble(key, defaultValue);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @param defaultValue Value to return if key does not exist
+ * @return an int value
+ */
+ public int getInt(String key, int defaultValue) {
+ return mBundle.getInt(key, defaultValue);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @param defaultValue Value to return if key does not exist
+ * @return a long value
+ */
+ public long getLong(String key, long defaultValue) {
+ return mBundle.getLong(key, defaultValue);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key or if a null
+ * value is explicitly associated with the given key.
+ *
+ * @param key a String
+ * @param defaultValue Value to return if key does not exist or if a null
+ * value is associated with the given key.
+ * @return the String value associated with the given key, or defaultValue
+ * if no valid String object is currently mapped to that key.
+ */
+ public String getString(String key, String defaultValue) {
+ return mBundle.getString(key, defaultValue);
+ }
+
+ /**
+ * Returns a Set containing the Strings used as keys in this Bundle.
+ *
+ * @return a Set of String keys
+ */
+ public Set<String> keySet() {
+ return mBundle.keySet();
+ }
+
+
+
+ public String toString() {
+ return mBundle.toString();
+ }
+
+}
+
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b85c911..1ee05b8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -49,6 +49,7 @@
import android.media.BufferingParams;
import android.media.MediaDrm;
import android.media.MediaFormat;
+import android.media.MediaMetricsSet;
import android.media.MediaTimeProvider;
import android.media.PlaybackParams;
import android.media.SubtitleController;
@@ -1494,70 +1495,22 @@
public native int getVideoHeight();
/**
- * Returns Analytics/Metrics data about the current video in this player.
+ * Return Metrics data about the current player.
*
- * @return the a map of attributes and values available for this video
- * player or null if no metrics are available.
+ * @return a MediaMetricsSet containing the set of attributes and values
+ * available for the media being handled by this instance of MediaPlayer
+ * The attributes are descibed in {@link MediaMetricsSet.MediaPlayer}.
*
- * <table style="width: 0%">
- * <thead>
- * <tr>
- * <th>Key</th>
- * <th>Type</th>
- * <th>Description</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td>{@code "video/codec"}</td>
- * <td>String</td>
- * <td>Identifies the video codec in use</td>
- * </tr><tr>
- * <td>{@code "video/mime"}</td>
- * <td>String</td>
- * <td>Mime type of the video being encoded/decoded</td>
- * </tr><tr>
- * <td>{@code "audio/codec"}</td>
- * <td>String</td>
- * <td>Identifies the audio codec in use</td>
- * </tr><tr>
- * <td>{@code "audio/mime"}</td>
- * <td>String</td>
- * <td>Mime type of the audio being encoded/decoded</td>
- * </tr><tr>
- * <td>{@code "ht"}</td>
- * <td>Integer</td>
- * <td>Height (pixels); valid only when mode=video</td>
- * </tr><tr>
- * <td>{@code "wid"}</td>
- * <td>Integer</td>
- * <td>Width (pixels); valid only when mode=video</td>
- * </tr><tr>
- * <td>{@code "frame"}</td>
- * <td>Integer</td>
- * <td>Number of decoded video frames sent to the display</td>
- * </tr><tr>
- * <td>{@code "dropped"}</td>
- * <td>Integer</td>
- * <td>Number of decoded video frames that were not sent to display.
- * These frames were dropped by the player.</td>
- * </tr><tr>
- * <td>{@code "durationMs"}</td>
- * <td>Integer</td>
- * <td>The length of the media being played (in ms), e.g. "This video lasts for 30000 milliseconds". </td>
- * </tr><tr>
- * <td>{@code "playingMs"}</td>
- * <td>Integer</td>
- * <td>The time the media has been played (in ms). If you watch a
- * 30 second twice through, this will report 60000 ms.</td>
- * </tr>
- * </tbody>
- * </table>
- *
- * Additional fields specific to individual codecs will also appear in
+ * Additional vendor-specific fields may also be present in
* the return value.
*/
- public native Bundle getMetrics();
+ public MediaMetricsSet getMetrics() {
+ Bundle bundle = native_getMetrics();
+ MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+ return mSet;
+ }
+
+ private native Bundle native_getMetrics();
/**
* Checks whether the MediaPlayer is playing.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 075a84f..cdc1d60 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.hardware.Camera;
+import android.media.MediaMetricsSet;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -1259,91 +1260,23 @@
private native void setParameter(String nameValuePair);
/**
- * Returns Metrics data about the current media container.
+ * Return Metrics data about the current Mediarecorder instance.
*
- * @return the set of keys and values available for the media being
- * handled by this instance of MediaExtractor. The keys, data types,
- * and meaning are described in the following table.
+ * @return a MediaMetricsSet containing the set of attributes and values
+ * available for the media being generated by this instance of
+ * MediaRecorder.
+ * The attributes are descibed in {@link MediaMetricsSet.MediaRecorder}.
*
- * <table style="width: 0%">
- * <thead>
- * <tr>
- * <th>Key</th>
- * <th>Type</th>
- * <th>Description</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td>{@code "ht"}</td>
- * <td>Integer</td>
- * <td>Height of the recorded video (pixels)</td>
- * </tr><tr>
- * <td>{@code "wid"}</td>
- * <td>Integer</td>
- * <td>Width of the recorded video (pixels)</td>
- * </tr><tr>
- * <td>{@code "frame-rate"}</td>
- * <td>Integer</td>
- * <td>Framerate of captured Video (frames per second)</td>
- * </tr><tr>
- * <td>{@code "video-bitrate"}</td>
- * <td>Integer</td>
- * <td>Bit rate of encoded video (bits per second)</td>
- * </tr><tr>
- * <td>{@code "video-iframe-interval"}</td>
- * <td>Integer</td>
- * <td>Interval between encoded IFrames (seconds)</td>
- * </tr><tr>
- * <td>{@code "video-timescale"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "video-encoder-profile"}</td>
- * <td>Integer</td>
- * <td>Video Encoder Profile, as defined in OpenMAX IL</td>
- * </tr><tr>
- * <td>{@code "video-encoder-level"}</td>
- * <td>Integer</td>
- * <td>Video Encoder Level, as defined in OpenMAX IL</td>
- * </tr><tr>
- * <td>{@code "audio-bitrate"}</td>
- * <td>Integer</td>
- * <td>Bitrate of encoded audio (bits per second)</td>
- * </tr><tr>
- * <td>{@code "audio-samplerate"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "audio-channels"}</td>
- * <td>Integer</td>
- * <td>Number of Audio Channels Captured</td>
- * </tr><tr>
- * <td>{@code "audio-timescale"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "movie-timescale"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "movie-timescale"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "capture-fps"}</td>
- * <td>Integer</td>
- * <td></td>
- * </tr><tr>
- * <td>{@code "rotation"}</td>
- * <td>Integer</td>
- * <td>Orientation of the Video (degrees)</td>
- * </tr>
- * </tbody>
- * </table>
+ * Additional vendor-specific fields may also be present in
+ * the return value.
*/
+ public MediaMetricsSet getMetrics() {
+ Bundle bundle = native_getMetrics();
+ MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+ return mSet;
+ }
- public native Bundle getMetrics();
+ private native Bundle native_getMetrics();
@Override
protected void finalize() { native_finalize(); }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 35988d4..a292b8e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -138,7 +138,6 @@
// Attributes from XML meta data.
private final String mSetupActivity;
- private final String mSettingsActivity;
private final boolean mCanRecord;
private final int mTunerCount;
@@ -259,9 +258,8 @@
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
- String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
- HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
- Bundle extras) {
+ String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
+ boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) {
mService = service;
mId = id;
mType = type;
@@ -272,7 +270,6 @@
mIconStandby = iconStandby;
mIconDisconnected = iconDisconnected;
mSetupActivity = setupActivity;
- mSettingsActivity = settingsActivity;
mCanRecord = canRecord;
mTunerCount = tunerCount;
mHdmiDeviceInfo = hdmiDeviceInfo;
@@ -340,14 +337,12 @@
/**
* Returns an intent to start the settings activity for this TV input.
+ *
+ * @deprecated Use {@link #createSetupIntent()} instead. Settings activity is deprecated.
+ * Use setup activity instead to provide settings.
*/
+ @Deprecated
public Intent createSettingsIntent() {
- if (!TextUtils.isEmpty(mSettingsActivity)) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
- intent.putExtra(EXTRA_INPUT_ID, getId());
- return intent;
- }
return null;
}
@@ -554,7 +549,6 @@
&& Objects.equals(mIconStandby, obj.mIconStandby)
&& Objects.equals(mIconDisconnected, obj.mIconDisconnected)
&& TextUtils.equals(mSetupActivity, obj.mSetupActivity)
- && TextUtils.equals(mSettingsActivity, obj.mSettingsActivity)
&& mCanRecord == obj.mCanRecord
&& mTunerCount == obj.mTunerCount
&& Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
@@ -589,7 +583,6 @@
dest.writeParcelable(mIconStandby, flags);
dest.writeParcelable(mIconDisconnected, flags);
dest.writeString(mSetupActivity);
- dest.writeString(mSettingsActivity);
dest.writeByte(mCanRecord ? (byte) 1 : 0);
dest.writeInt(mTunerCount);
dest.writeParcelable(mHdmiDeviceInfo, flags);
@@ -631,7 +624,6 @@
mIconStandby = in.readParcelable(null);
mIconDisconnected = in.readParcelable(null);
mSetupActivity = in.readString();
- mSettingsActivity = in.readString();
mCanRecord = in.readByte() == 1;
mTunerCount = in.readInt();
mHdmiDeviceInfo = in.readParcelable(null);
@@ -678,7 +670,6 @@
private Icon mIconStandby;
private Icon mIconDisconnected;
private String mSetupActivity;
- private String mSettingsActivity;
private Boolean mCanRecord;
private Integer mTunerCount;
private TvInputHardwareInfo mTvInputHardwareInfo;
@@ -906,7 +897,7 @@
}
parseServiceMetadata(type);
return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
- mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
+ mIcon, mIconStandby, mIconDisconnected, mSetupActivity,
mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
}
@@ -961,8 +952,6 @@
if (inputType == TYPE_TUNER && TextUtils.isEmpty(mSetupActivity)) {
throw new IllegalStateException("Setup activity not found for " + si.name);
}
- mSettingsActivity = sa.getString(
- com.android.internal.R.styleable.TvInputService_settingsActivity);
if (mCanRecord == null) {
mCanRecord = sa.getBoolean(
com.android.internal.R.styleable.TvInputService_canRecord, false);
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 293e5dd..a8dd313 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -624,7 +624,7 @@
return OK;
}
-status_t JMediaCodec::getMetrics(JNIEnv *, Parcel *reply) const {
+status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const {
status_t status = mCodec->getMetrics(reply);
return status;
@@ -1666,9 +1666,9 @@
}
static jobject
-android_media_MediaCodec_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_MediaCodec_getMetrics");
+ ALOGV("android_media_MediaCodec_native_getMetrics");
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL ) {
@@ -1677,16 +1677,14 @@
}
// get what we have for the metrics from the codec
- Parcel reply;
- status_t err = codec->getMetrics(env, &reply);
+ MediaAnalyticsItem *item = NULL;
+
+ status_t err = codec->getMetrics(env, item);
if (err != OK) {
ALOGE("getMetrics failed");
return (jobject) NULL;
}
- // build and return the Bundle
- MediaAnalyticsItem *item = new MediaAnalyticsItem;
- item->readFromParcel(reply);
jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
// housekeeping
@@ -2004,8 +2002,8 @@
{ "getName", "()Ljava/lang/String;",
(void *)android_media_MediaCodec_getName },
- { "getMetrics", "()Landroid/os/Bundle;",
- (void *)android_media_MediaCodec_getMetrics},
+ { "native_getMetrics", "()Landroid/os/Bundle;",
+ (void *)android_media_MediaCodec_native_getMetrics},
{ "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
(void *)android_media_MediaCodec_setParameters },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a8c76c5..c9a1700 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -19,6 +19,7 @@
#include "jni.h"
+#include <media/MediaAnalyticsItem.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AHandler.h>
@@ -116,7 +117,7 @@
status_t getName(JNIEnv *env, jstring *name) const;
- status_t getMetrics(JNIEnv *env, Parcel *reply) const;
+ status_t getMetrics(JNIEnv *env, MediaAnalyticsItem * &reply) const;
status_t setParameters(const sp<AMessage> ¶ms);
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 3c33493..c2cfed9 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -811,9 +811,9 @@
}
static jobject
-android_media_MediaExtractor_getMetrics(JNIEnv * env, jobject thiz)
+android_media_MediaExtractor_native_getMetrics(JNIEnv * env, jobject thiz)
{
- ALOGV("android_media_MediaExtractor_getMetrics");
+ ALOGV("android_media_MediaExtractor_native_getMetrics");
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
if (extractor == NULL ) {
@@ -905,8 +905,8 @@
{ "hasCacheReachedEndOfStream", "()Z",
(void *)android_media_MediaExtractor_hasCacheReachedEOS },
- {"getMetrics", "()Landroid/os/Bundle;",
- (void *)android_media_MediaExtractor_getMetrics},
+ {"native_getMetrics", "()Landroid/os/Bundle;",
+ (void *)android_media_MediaExtractor_native_getMetrics},
};
int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 27724a1..1b52cf5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -708,7 +708,7 @@
}
static jobject
-android_media_MediaPlayer_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer_native_getMetrics(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -1393,7 +1393,7 @@
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
- {"getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaPlayer_getMetrics},
+ {"native_getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaPlayer_native_getMetrics},
{"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
{"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
{"setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer_setSyncParams},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 77544eb..7a63e00 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -628,9 +628,9 @@
}
static jobject
-android_media_MediaRecorder_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaRecorder_native_getMetrics(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_MediaRecorder_getMetrics");
+ ALOGV("android_media_MediaRecorder_native_getMetrics");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if (mr == NULL) {
@@ -688,7 +688,7 @@
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
{"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
- {"getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaRecorder_getMetrics},
+ {"native_getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaRecorder_native_getMetrics},
};
// This function only registers the native methods, and is called from
diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h
index 803ed29..9b57958 100644
--- a/media/mca/filterfw/jni/jni_util.h
+++ b/media/mca/filterfw/jni/jni_util.h
@@ -198,7 +198,8 @@
CObjMap objects_;
FlagMap owns_;
- DISALLOW_COPY_AND_ASSIGN(ObjectPool);
+ ObjectPool(const ObjectPool&) = delete;
+ ObjectPool& operator=(const ObjectPool&) = delete;
};
template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL;
diff --git a/media/mca/filterfw/native/Android.mk b/media/mca/filterfw/native/Android.mk
index 2e900fe..feaefcb 100644
--- a/media/mca/filterfw/native/Android.mk
+++ b/media/mca/filterfw/native/Android.mk
@@ -41,7 +41,11 @@
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_STATIC_LIBRARIES := libarect
+LOCAL_STATIC_LIBRARIES := \
+ libarect \
+
+LOCAL_SHARED_LIBRARIES += \
+ libgui \
# TODO: Build a shared library as well?
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/mca/filterfw/native/base/utilities.h b/media/mca/filterfw/native/base/utilities.h
index 6bb3b7f..2818f72 100644
--- a/media/mca/filterfw/native/base/utilities.h
+++ b/media/mca/filterfw/native/base/utilities.h
@@ -23,18 +23,6 @@
namespace android {
namespace filterfw {
-// Convenience Macro to make copy constructor and assignment operator private
-// (thereby disallowing copying and assigning).
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
// STLDeleteContainerPointers()
// For a range within a container of pointers, calls delete
// (non-array version) on these pointers.
diff --git a/media/mca/filterfw/native/core/gl_env.h b/media/mca/filterfw/native/core/gl_env.h
index 0445301..6af91af 100644
--- a/media/mca/filterfw/native/core/gl_env.h
+++ b/media/mca/filterfw/native/core/gl_env.h
@@ -256,7 +256,8 @@
std::map<int, ShaderProgram*> attached_shaders_;
std::map<int, VertexFrame*> attached_vframes_;
- DISALLOW_COPY_AND_ASSIGN(GLEnv);
+ GLEnv(const GLEnv&) = delete;
+ GLEnv& operator=(const GLEnv&) = delete;
};
} // namespace filterfw
diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h
index 2da557d..fd52165 100644
--- a/media/mca/filterfw/native/core/native_frame.h
+++ b/media/mca/filterfw/native/core/native_frame.h
@@ -76,7 +76,8 @@
// Capacity of data buffer in bytes.
int capacity_;
- DISALLOW_COPY_AND_ASSIGN(NativeFrame);
+ NativeFrame(const NativeFrame&) = delete;
+ NativeFrame& operator=(const NativeFrame&) = delete;
};
} // namespace filterfw
diff --git a/media/mca/filterfw/native/core/native_program.h b/media/mca/filterfw/native/core/native_program.h
index ce704af..e39afc9 100644
--- a/media/mca/filterfw/native/core/native_program.h
+++ b/media/mca/filterfw/native/core/native_program.h
@@ -75,7 +75,8 @@
// Pointer to user data
void* user_data_;
- DISALLOW_COPY_AND_ASSIGN(NativeProgram);
+ NativeProgram(const NativeProgram&) = delete;
+ NativeProgram& operator=(const NativeProgram&) = delete;
};
} // namespace filterfw
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 2e642ec..8df194c 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -25,7 +25,6 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<application android:label="@string/app_name" >
@@ -34,10 +33,16 @@
<action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
</intent-filter>
</receiver>
- <activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity"
- android:theme="@android:style/Theme.Translucent.NoTitleBar"
- android:excludeFromRecents="true"/>
<service android:name="com.android.carrierdefaultapp.ProvisionObserver"
android:permission="android.permission.BIND_JOB_SERVICE"/>
+ <activity
+ android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
+ android:label="@string/action_bar_label"
+ android:theme="@style/AppTheme"
+ android:configChanges="keyboardHidden|orientation|screenSize" >
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png
new file mode 100644
index 0000000..08294ce
--- /dev/null
+++ b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png
Binary files differ
diff --git a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
index dc54fe2..75aa405 100644
--- a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
+++ b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
@@ -22,4 +22,4 @@
<path
android:fillColor="#757575"
android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml
new file mode 100644
index 0000000..528576b
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml
@@ -0,0 +1,34 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
+ tools:ignore="MergeRootFrame">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/url_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:singleLine="true" />
+
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleHorizontal" />
+
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="false"
+ android:layout_alignParentRight="false" />
+
+</LinearLayout>
+</FrameLayout>
diff --git a/packages/CarrierDefaultApp/res/values/dimens.xml b/packages/CarrierDefaultApp/res/values/dimens.xml
index a3c5049..1ea8c35 100644
--- a/packages/CarrierDefaultApp/res/values/dimens.xml
+++ b/packages/CarrierDefaultApp/res/values/dimens.xml
@@ -1,3 +1,6 @@
<resources>
<dimen name="glif_icon_size">32dp</dimen>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
</resources>
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index f904600..f9342b7 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -6,9 +6,8 @@
<string name="no_data_notification_id">Your mobile data has been deactivated</string>
<string name="portal_notification_detail">Tap to visit the %s website</string>
<string name="no_data_notification_detail">Please contact your service provider %s</string>
- <string name="progress_dialogue_network_connection">Connecting to captive portal...</string>
- <string name="alert_dialogue_network_timeout">Network timeout, would you like to retry?</string>
- <string name="alert_dialogue_network_timeout_title">Network unavailable</string>
- <string name="quit">Quit</string>
- <string name="wait">Wait</string>
+ <string name="action_bar_label">Sign in to mobile network</string>
+ <string name="ssl_error_warning">The network you’re trying to join has security issues.</string>
+ <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
+ <string name="ssl_error_continue">Continue anyway via browser</string>
</resources>
diff --git a/packages/CarrierDefaultApp/res/values/styles.xml b/packages/CarrierDefaultApp/res/values/styles.xml
index 3d26915..939c1aa 100644
--- a/packages/CarrierDefaultApp/res/values/styles.xml
+++ b/packages/CarrierDefaultApp/res/values/styles.xml
@@ -1,3 +1,16 @@
<resources>
- <style name="AlertDialog" parent="android:Theme.Material.Light.Dialog.Alert"/>
+ <style name="AppBaseTheme" parent="@android:style/Theme.Material.Settings">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ <!-- Setting's theme's accent color makes ProgressBar useless, reset back. -->
+ <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
+ </style>
</resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java
deleted file mode 100644
index 28251cb..0000000
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.carrierdefaultapp;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.CaptivePortal;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-import android.os.Bundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
-import android.net.ICaptivePortal;
-import android.view.ContextThemeWrapper;
-import android.view.WindowManager;
-import com.android.carrierdefaultapp.R;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.ArrayUtils;
-
-import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
-
-/**
- * Activity that launches in response to the captive portal notification
- * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
- * This activity requests network connection if there is no available one, launches the
- * {@link com.android.captiveportallogin portalApp} and keeps track of the portal activation result.
- */
-public class CaptivePortalLaunchActivity extends Activity {
- private static final String TAG = CaptivePortalLaunchActivity.class.getSimpleName();
- private static final boolean DBG = true;
- public static final int NETWORK_REQUEST_TIMEOUT_IN_MS = 5 * 1000;
-
- private ConnectivityManager mCm = null;
- private ConnectivityManager.NetworkCallback mCb = null;
- /* Progress dialogue when request network connection for captive portal */
- private AlertDialog mProgressDialog = null;
- /* Alert dialogue when network request is timeout */
- private AlertDialog mAlertDialog = null;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mCm = ConnectivityManager.from(this);
- // Check network connection before loading portal
- Network network = getNetworkForCaptivePortal();
- NetworkInfo nwInfo = mCm.getNetworkInfo(network);
- if (nwInfo == null || !nwInfo.isConnected()) {
- if (DBG) logd("Network unavailable, request restricted connection");
- requestNetwork(getIntent());
- } else {
- launchCaptivePortal(getIntent(), network);
- }
- }
-
- // show progress dialog during network connecting
- private void showConnectingProgressDialog() {
- mProgressDialog = new ProgressDialog(getApplicationContext());
- mProgressDialog.setMessage(getString(R.string.progress_dialogue_network_connection));
- mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
- mProgressDialog.show();
- }
-
- // if network request is timeout, show alert dialog with two option: cancel & wait
- private void showConnectionTimeoutAlertDialog() {
- mAlertDialog = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialog))
- .setMessage(getString(R.string.alert_dialogue_network_timeout))
- .setTitle(getString(R.string.alert_dialogue_network_timeout_title))
- .setNegativeButton(getString(R.string.quit),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // cancel
- dismissDialog(mAlertDialog);
- finish();
- }
- })
- .setPositiveButton(getString(R.string.wait),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // wait, request network again
- dismissDialog(mAlertDialog);
- requestNetwork(getIntent());
- }
- })
- .create();
- mAlertDialog.show();
- }
-
- private void requestNetwork(final Intent intent) {
- NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
-
- mCb = new ConnectivityManager.NetworkCallback() {
- @Override
- public void onAvailable(Network network) {
- if (DBG) logd("Network available: " + network);
- dismissDialog(mProgressDialog);
- mCm.bindProcessToNetwork(network);
- launchCaptivePortal(intent, network);
- }
-
- @Override
- public void onUnavailable() {
- if (DBG) logd("Network unavailable");
- dismissDialog(mProgressDialog);
- showConnectionTimeoutAlertDialog();
- }
- };
- showConnectingProgressDialog();
- mCm.requestNetwork(request, mCb, NETWORK_REQUEST_TIMEOUT_IN_MS);
- }
-
- private void releaseNetworkRequest() {
- logd("release Network Request");
- if (mCb != null) {
- mCm.unregisterNetworkCallback(mCb);
- mCb = null;
- }
- }
-
- private void dismissDialog(AlertDialog dialog) {
- if (dialog != null) {
- dialog.dismiss();
- }
- }
-
- private Network getNetworkForCaptivePortal() {
- Network[] info = mCm.getAllNetworks();
- if (!ArrayUtils.isEmpty(info)) {
- for (Network nw : info) {
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw);
- if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- return nw;
- }
- }
- }
- return null;
- }
-
- private void launchCaptivePortal(final Intent intent, Network network) {
- String redirectUrl = intent.getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.getDefaultVoiceSubscriptionId());
- if (TextUtils.isEmpty(redirectUrl) || !matchUrl(redirectUrl, subId)) {
- loge("Launch portal fails due to incorrect redirection URL: " +
- Rlog.pii(TAG, redirectUrl));
- return;
- }
- final Intent portalIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
- portalIntent.putExtra(ConnectivityManager.EXTRA_NETWORK, network);
- portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
- new CaptivePortal(new ICaptivePortal.Stub() {
- @Override
- public void appResponse(int response) {
- logd("portal response code: " + response);
- releaseNetworkRequest();
- if (response == APP_RETURN_DISMISSED) {
- // Upon success http response code, trigger re-evaluation
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, intent,
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, intent,
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS,
- intent, getApplicationContext());
- }
- }
- }));
- portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, redirectUrl);
- portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- if (DBG) logd("launching portal");
- startActivity(portalIntent);
- finish();
- }
-
- // match configured redirection url
- private boolean matchUrl(String url, int subId) {
- CarrierConfigManager configManager = getApplicationContext()
- .getSystemService(CarrierConfigManager.class);
- String[] redirectURLs = configManager.getConfigForSubId(subId).getStringArray(
- CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
- if (ArrayUtils.isEmpty(redirectURLs)) {
- if (DBG) logd("match is unnecessary without any configured redirection url");
- return true;
- }
- for (String redirectURL : redirectURLs) {
- if (url.startsWith(redirectURL)) {
- return true;
- }
- }
- if (DBG) loge("no match found for configured redirection url");
- return false;
- }
-
- private static void logd(String s) {
- Rlog.d(TAG, s);
- }
-
- private static void loge(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
new file mode 100644
index 0000000..a5820f2
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.carrierdefaultapp;
+
+import android.app.Activity;
+import android.app.LoadedApk;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.Proxy;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.TypedValue;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Random;
+
+/**
+ * Activity that launches in response to the captive portal notification
+ * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
+ * This activity requests network connection if there is no available one before loading the real
+ * portal page and apply carrier actions on the portal activation result.
+ */
+public class CaptivePortalLoginActivity extends Activity {
+ private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
+ private static final boolean DBG = true;
+
+ private static final int SOCKET_TIMEOUT_MS = 10 * 1000;
+ public static final int NETWORK_REQUEST_TIMEOUT_MS = 5 * 1000;
+
+ private URL mUrl;
+ private Network mNetwork;
+ private NetworkCallback mNetworkCallback;
+ private ConnectivityManager mCm;
+ private WebView mWebView;
+ private MyWebViewClient mWebViewClient;
+ private boolean mLaunchBrowser = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCm = ConnectivityManager.from(this);
+ mUrl = getUrlForCaptivePortal();
+ if (mUrl == null) {
+ done(false);
+ return;
+ }
+ if (DBG) logd(String.format("onCreate for %s", mUrl.toString()));
+ setContentView(R.layout.activity_captive_portal_login);
+ getActionBar().setDisplayShowHomeEnabled(false);
+
+ mWebView = (WebView) findViewById(R.id.webview);
+ mWebView.clearCache(true);
+ WebSettings webSettings = mWebView.getSettings();
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
+ mWebViewClient = new MyWebViewClient();
+ mWebView.setWebViewClient(mWebViewClient);
+ mWebView.setWebChromeClient(new MyWebChromeClient());
+
+ mNetwork = getNetworkForCaptivePortal();
+ if (mNetwork == null) {
+ requestNetworkForCaptivePortal();
+ } else {
+ mCm.bindProcessToNetwork(mNetwork);
+ // Start initial page load so WebView finishes loading proxy settings.
+ // Actual load of mUrl is initiated by MyWebViewClient.
+ mWebView.loadData("", "text/html", null);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ WebView myWebView = (WebView) findViewById(R.id.webview);
+ if (myWebView.canGoBack() && mWebViewClient.allowBack()) {
+ myWebView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ releaseNetworkRequest();
+ if (mLaunchBrowser) {
+ // Give time for this network to become default. After 500ms just proceed.
+ for (int i = 0; i < 5; i++) {
+ // TODO: This misses when mNetwork underlies a VPN.
+ if (mNetwork.equals(mCm.getActiveNetwork())) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ final String url = mUrl.toString();
+ if (DBG) logd("starting activity with intent ACTION_VIEW for " + url);
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+ }
+ }
+
+ // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
+ private void setWebViewProxy() {
+ LoadedApk loadedApk = getApplication().mLoadedApk;
+ try {
+ Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
+ receiversField.setAccessible(true);
+ ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
+ for (Object receiverMap : receivers.values()) {
+ for (Object rec : ((ArrayMap) receiverMap).keySet()) {
+ Class clazz = rec.getClass();
+ if (clazz.getName().contains("ProxyChangeListener")) {
+ Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
+ Intent.class);
+ Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
+ onReceiveMethod.invoke(rec, getApplicationContext(), intent);
+ Log.v(TAG, "Prompting WebView proxy reload.");
+ }
+ }
+ }
+ } catch (Exception e) {
+ loge("Exception while setting WebView proxy: " + e);
+ }
+ }
+
+ private void done(boolean success) {
+ if (DBG) logd(String.format("Result success %b for %s", success, mUrl.toString()));
+ if (success) {
+ // Trigger re-evaluation upon success http response code
+ CarrierActionUtils.applyCarrierAction(
+ CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, getIntent(),
+ getApplicationContext());
+ CarrierActionUtils.applyCarrierAction(
+ CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, getIntent(),
+ getApplicationContext());
+ CarrierActionUtils.applyCarrierAction(
+ CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(),
+ getApplicationContext());
+
+ }
+ finishAndRemoveTask();
+ }
+
+ private URL getUrlForCaptivePortal() {
+ String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
+ if (url.isEmpty()) {
+ url = mCm.getCaptivePortalServerUrl();
+ }
+ final CarrierConfigManager configManager = getApplicationContext()
+ .getSystemService(CarrierConfigManager.class);
+ final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ final String[] portalURLs = configManager.getConfigForSubId(subId).getStringArray(
+ CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
+ if (!ArrayUtils.isEmpty(portalURLs)) {
+ for (String portalUrl : portalURLs) {
+ if (url.startsWith(portalUrl)) {
+ break;
+ }
+ }
+ url = null;
+ }
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ loge("Invalid captive portal URL " + url);
+ }
+ return null;
+ }
+
+ private void testForCaptivePortal() {
+ new Thread(new Runnable() {
+ public void run() {
+ // Give time for captive portal to open.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ HttpURLConnection urlConnection = null;
+ int httpResponseCode = 500;
+ int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+ try {
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
+ urlConnection.setInstanceFollowRedirects(false);
+ urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setUseCaches(false);
+ urlConnection.getInputStream();
+ httpResponseCode = urlConnection.getResponseCode();
+ } catch (IOException e) {
+ } finally {
+ if (urlConnection != null) urlConnection.disconnect();
+ TrafficStats.setThreadStatsTag(oldTag);
+ }
+ if (httpResponseCode == 204) {
+ done(true);
+ }
+ }
+ }).start();
+ }
+
+ private Network getNetworkForCaptivePortal() {
+ Network[] info = mCm.getAllNetworks();
+ if (!ArrayUtils.isEmpty(info)) {
+ for (Network nw : info) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw);
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ return nw;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void requestNetworkForCaptivePortal() {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .build();
+
+ mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ if (DBG) logd("Network available: " + network);
+ mCm.bindProcessToNetwork(network);
+ mNetwork = network;
+ runOnUiThreadIfNotFinishing(() -> {
+ // Start initial page load so WebView finishes loading proxy settings.
+ // Actual load of mUrl is initiated by MyWebViewClient.
+ mWebView.loadData("", "text/html", null);
+ });
+ }
+
+ @Override
+ public void onUnavailable() {
+ if (DBG) logd("Network unavailable");
+ runOnUiThreadIfNotFinishing(() -> {
+ // Instead of not loading anything in webview, simply load the page and return
+ // HTTP error page in the absence of network connection.
+ mWebView.loadUrl(mUrl.toString());
+ });
+ }
+ };
+ logd("request Network for captive portal");
+ mCm.requestNetwork(request, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MS);
+ }
+
+ private void releaseNetworkRequest() {
+ logd("release Network for captive portal");
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
+ mNetwork = null;
+ }
+ }
+
+ private class MyWebViewClient extends WebViewClient {
+ private static final String INTERNAL_ASSETS = "file:///android_asset/";
+ private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
+ // How many Android device-independent-pixels per scaled-pixel
+ // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
+ private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+ getResources().getDisplayMetrics())
+ / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
+ getResources().getDisplayMetrics());
+ private int mPagesLoaded;
+
+ // If we haven't finished cleaning up the history, don't allow going back.
+ public boolean allowBack() {
+ return mPagesLoaded > 1;
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ if (url.contains(mBrowserBailOutToken)) {
+ mLaunchBrowser = true;
+ done(false);
+ return;
+ }
+ // The first page load is used only to cause the WebView to
+ // fetch the proxy settings. Don't update the URL bar, and
+ // don't check if the captive portal is still there.
+ if (mPagesLoaded == 0) return;
+ // For internally generated pages, leave URL bar listing prior URL as this is the URL
+ // the page refers to.
+ if (!url.startsWith(INTERNAL_ASSETS)) {
+ final TextView myUrlBar = (TextView) findViewById(R.id.url_bar);
+ myUrlBar.setText(url);
+ }
+ if (mNetwork != null) {
+ testForCaptivePortal();
+ }
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ mPagesLoaded++;
+ if (mPagesLoaded == 1) {
+ // Now that WebView has loaded at least one page we know it has read in the proxy
+ // settings. Now prompt the WebView read the Network-specific proxy settings.
+ setWebViewProxy();
+ // Load the real page.
+ view.loadUrl(mUrl.toString());
+ return;
+ } else if (mPagesLoaded == 2) {
+ // Prevent going back to empty first page.
+ view.clearHistory();
+ }
+ if (mNetwork != null) {
+ testForCaptivePortal();
+ }
+ }
+
+ // Convert Android device-independent-pixels (dp) to HTML size.
+ private String dp(int dp) {
+ // HTML px's are scaled just like dp's, so just add "px" suffix.
+ return Integer.toString(dp) + "px";
+ }
+
+ // Convert Android scaled-pixels (sp) to HTML size.
+ private String sp(int sp) {
+ // Convert sp to dp's.
+ float dp = sp * mDpPerSp;
+ // Apply a scale factor to make things look right.
+ dp *= 1.3;
+ // Convert dp's to HTML size.
+ return dp((int) dp);
+ }
+
+ // A web page consisting of a large broken lock icon to indicate SSL failure.
+ private final String SSL_ERROR_HTML = "<html><head><style>"
+ + "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; "
+ + "margin-top:" + dp(96) + "; background-color:#fafafa; }"
+ + "img { width:" + dp(48) + "; height:" + dp(48) + "; }"
+ + "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; "
+ + " opacity:0.87; line-height:1.28; }"
+ + "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; "
+ + " opacity:0.54; line-height:1.21905; }"
+ + "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; "
+ + " margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; "
+ + " height:" + dp(48) + "; font-weight:bold; }"
+ + "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>"
+ + "<div class=warn>%s</div>"
+ + "<div class=example>%s</div>" + "<a href=%s>%s</a></body></html>";
+
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+ Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: "
+ // Only show host to avoid leaking private info.
+ + Uri.parse(error.getUrl()).getHost() + " certificate: "
+ + error.getCertificate() + "); displaying SSL warning.");
+ final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
+ getString(R.string.ssl_error_example), mBrowserBailOutToken,
+ getString(R.string.ssl_error_continue));
+ view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (url.startsWith("tel:")) {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private class MyWebChromeClient extends WebChromeClient {
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
+ myProgressBar.setProgress(newProgress);
+ }
+ }
+
+ private void runOnUiThreadIfNotFinishing(Runnable r) {
+ if (!isFinishing()) {
+ runOnUiThread(r);
+ }
+ }
+
+ private static void logd(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private static void loge(String s) {
+ Rlog.d(TAG, s);
+ }
+
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index d9bd2fc..73ff3a9 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -112,8 +112,10 @@
logd("onShowCaptivePortalNotification");
final NotificationManager notificationMgr = context.getSystemService(
NotificationManager.class);
- Intent portalIntent = new Intent(context, CaptivePortalLaunchActivity.class);
+ Intent portalIntent = new Intent(context, CaptivePortalLoginActivity.class);
portalIntent.putExtras(intent);
+ portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
+ | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, portalIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = getNotification(context, R.string.portal_notification_id,
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java
deleted file mode 100644
index 8a18d72..0000000
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.android.carrierdefaultapp;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class LaunchCaptivePortalActivityTest extends
- CarrierDefaultActivityTestCase<CaptivePortalLaunchActivity> {
-
- @Mock
- private ConnectivityManager mCm;
- @Mock
- private NetworkInfo mNetworkInfo;
- @Mock
- private Network mNetwork;
-
- @Captor
- private ArgumentCaptor<Integer> mInt;
- @Captor
- private ArgumentCaptor<NetworkRequest> mNetworkReq;
-
- private NetworkCapabilities mNetworkCapabilities;
-
- public LaunchCaptivePortalActivityTest() {
- super(CaptivePortalLaunchActivity.class);
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- injectSystemService(ConnectivityManager.class, mCm);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @Override
- protected Intent createActivityIntent() {
- Intent intent = new Intent(getInstrumentation().getTargetContext(),
- CaptivePortalLaunchActivity.class);
- intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, "url");
- return intent;
- }
-
- @Test
- public void testWithoutInternetConnection() throws Throwable {
- startActivity();
- TestContext.waitForMs(100);
- verify(mCm, atLeast(1)).requestNetwork(mNetworkReq.capture(), any(), mInt.capture());
- // verify network request
- assert(mNetworkReq.getValue().networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET));
- assert(mNetworkReq.getValue().networkCapabilities.hasTransport(
- NetworkCapabilities.TRANSPORT_CELLULAR));
- assertFalse(mNetworkReq.getValue().networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
- assertEquals(CaptivePortalLaunchActivity.NETWORK_REQUEST_TIMEOUT_IN_MS,
- (int) mInt.getValue());
- // verify captive portal app is not launched due to unavailable network
- assertNull(getStartedActivityIntent());
- stopActivity();
- }
-
- @Test
- public void testWithInternetConnection() throws Throwable {
- // Mock internet connection
- mNetworkCapabilities = new NetworkCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- doReturn(new Network[]{mNetwork}).when(mCm).getAllNetworks();
- doReturn(mNetworkCapabilities).when(mCm).getNetworkCapabilities(eq(mNetwork));
- doReturn(mNetworkInfo).when(mCm).getNetworkInfo(eq(mNetwork));
- doReturn(true).when(mNetworkInfo).isConnected();
-
- startActivity();
- TestContext.waitForMs(100);
- // verify there is no network request with internet connection
- verify(mCm, times(0)).requestNetwork(any(), any(), anyInt());
- // verify captive portal app is launched
- assertNotNull(getStartedActivityIntent());
- assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN,
- getStartedActivityIntent().getAction());
- stopActivity();
- }
-}
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 5e49d05..c6837fa 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -15,50 +15,55 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/menu"
+ android:id="@+id/background"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#4D000000">
- <!-- The above background is only for the dismiss button ripple to show. -->
+ android:layout_height="match_parent">
- <ImageView
- android:id="@+id/dismiss"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="top|end"
- android:padding="@dimen/pip_action_padding"
- android:contentDescription="@string/pip_phone_close"
- android:src="@drawable/ic_close_white"
- android:background="?android:selectableItemBackgroundBorderless" />
+ <!-- Menu layout -->
+ <FrameLayout
+ android:id="@+id/menu_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <!-- The margins for this container is calculated in the code depending on whether the
- actions_container is visible. -->
- <FrameLayout
- android:id="@+id/expand_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/expand_button"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:layout_gravity="center"
- android:contentDescription="@string/pip_phone_expand"
- android:background="?android:selectableItemBackgroundBorderless" />
- </FrameLayout>
+ <ImageView
+ android:id="@+id/dismiss"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:layout_gravity="top|end"
+ android:padding="@dimen/pip_action_padding"
+ android:contentDescription="@string/pip_phone_close"
+ android:src="@drawable/ic_close_white"
+ android:background="?android:selectableItemBackgroundBorderless" />
- <FrameLayout
- android:id="@+id/actions_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="bottom"
- android:visibility="invisible">
- <LinearLayout
- android:id="@+id/actions_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:orientation="horizontal"
- android:divider="@android:color/transparent"
- android:showDividers="middle" />
- </FrameLayout>
+ <!-- The margins for this container is calculated in the code depending on whether the
+ actions_container is visible. -->
+ <FrameLayout
+ android:id="@+id/expand_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/expand_button"
+ android:layout_width="60dp"
+ android:layout_height="60dp"
+ android:layout_gravity="center"
+ android:contentDescription="@string/pip_phone_expand"
+ android:background="?android:selectableItemBackgroundBorderless" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/pip_action_size"
+ android:layout_gravity="bottom"
+ android:visibility="invisible">
+ <LinearLayout
+ android:id="@+id/actions_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:divider="@android:color/transparent"
+ android:showDividers="middle" />
+ </FrameLayout>
+ </FrameLayout>
</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 86e2c49..2f9c3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -20,10 +20,12 @@
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
+import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -35,6 +37,8 @@
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
@@ -71,13 +75,19 @@
public static final int MESSAGE_POKE_MENU = 2;
public static final int MESSAGE_HIDE_MENU = 3;
public static final int MESSAGE_UPDATE_ACTIONS = 4;
+ public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
private static final long INITIAL_DISMISS_DELAY = 2000;
private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
private static final long MENU_FADE_DURATION = 125;
+ private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+ private static final float DISMISS_BACKGROUND_ALPHA = 0.8f;
+
private boolean mMenuVisible;
private final List<RemoteAction> mActions = new ArrayList<>();
+ private View mViewRoot;
+ private Drawable mBackgroundDrawable;
private View mMenuContainer;
private LinearLayout mActionsGroup;
private View mDismissButton;
@@ -85,6 +95,14 @@
private int mBetweenActionPaddingLand;
private ObjectAnimator mMenuContainerAnimator;
+ private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float alpha = (float) animation.getAnimatedValue();
+ mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
+ }
+ };
private PointF mDownPosition = new PointF();
private PointF mDownDelta = new PointF();
@@ -109,6 +127,10 @@
Pair<Rect, ParceledListSlice> data = (Pair<Rect, ParceledListSlice>) msg.obj;
setActions(data.first, data.second.getList());
break;
+ case MESSAGE_UPDATE_DISMISS_FRACTION:
+ float fraction = (float) msg.obj;
+ updateDismissFraction(fraction);
+ break;
}
}
});
@@ -130,7 +152,12 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.pip_menu_activity);
- mMenuContainer = findViewById(R.id.menu);
+ mBackgroundDrawable = new ColorDrawable(Color.BLACK);
+ mBackgroundDrawable.setAlpha(0);
+ mViewRoot = findViewById(R.id.background);
+ mViewRoot.setBackground(mBackgroundDrawable);
+ mMenuContainer = findViewById(R.id.menu_container);
+ mMenuContainer.setAlpha(0);
mMenuContainer.setOnClickListener((v) -> {
expandPip();
});
@@ -222,10 +249,10 @@
private void showMenu(Rect stackBounds, Rect movementBounds) {
if (!mMenuVisible) {
+ updateActionViews(stackBounds);
if (mMenuContainerAnimator != null) {
mMenuContainerAnimator.cancel();
}
-
notifyMenuVisibility(true);
updateExpandButtonFromBounds(stackBounds, movementBounds);
mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -238,6 +265,7 @@
repostDelayedFinish(INITIAL_DISMISS_DELAY);
}
});
+ mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
mMenuContainerAnimator.start();
} else {
repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
@@ -269,20 +297,24 @@
}
}
});
+ mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
mMenuContainerAnimator.start();
}
}
private void updateFromIntent(Intent intent) {
- Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
- Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
- EXTRA_MOVEMENT_BOUNDS));
mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
if (actions != null) {
- setActions(stackBounds, actions.getList());
+ mActions.clear();
+ mActions.addAll(actions.getList());
}
- showMenu(stackBounds, movementBounds);
+ if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+ Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
+ Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
+ EXTRA_MOVEMENT_BOUNDS));
+ showMenu(stackBounds, movementBounds);
+ }
}
private void updateExpandButtonFromBounds(Rect stackBounds, Rect movementBounds) {
@@ -365,6 +397,19 @@
}
}
+ private void updateDismissFraction(float fraction) {
+ int alpha;
+ if (mMenuVisible) {
+ mMenuContainer.setAlpha(1-fraction);
+ final float interpolatedAlpha =
+ MENU_BACKGROUND_ALPHA * (1.0f - fraction) + DISMISS_BACKGROUND_ALPHA * fraction;
+ alpha = (int) (interpolatedAlpha*255);
+ } else {
+ alpha = (int) (fraction*DISMISS_BACKGROUND_ALPHA*255);
+ }
+ mBackgroundDrawable.setAlpha(alpha);
+ }
+
private void notifyRegisterInputConsumer() {
Message m = Message.obtain();
m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index badf64b..7dc455b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -42,7 +42,7 @@
import java.util.List;
/**
- * Manages the PiP menu activity.
+ * Manages the PiP menu activity which can show menu options or a scrim.
*
* The current media session provides actions whenever there are no valid actions provided by the
* current PiP activity. Otherwise, those actions always take precedence.
@@ -55,6 +55,7 @@
public static final String EXTRA_ACTIONS = "actions";
public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
public static final String EXTRA_MOVEMENT_BOUNDS = "movement_bounds";
+ public static final String EXTRA_SHOW_MENU = "show_menu";
public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
public static final int MESSAGE_EXPAND_PIP = 101;
@@ -101,6 +102,7 @@
private ParceledListSlice mMediaActions;
private boolean mMenuVisible;
+ private boolean mStartActivityRequested;
private Messenger mToActivityMessenger;
private Messenger mMessenger = new Messenger(new Handler() {
@Override
@@ -135,6 +137,7 @@
}
case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
mToActivityMessenger = msg.replyTo;
+ mStartActivityRequested = false;
// Mark the menu as invisible once the activity finishes as well
if (mToActivityMessenger == null) {
onMenuVisibilityChanged(false, true /* resize */);
@@ -179,6 +182,25 @@
}
/**
+ * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
+ */
+ public void setDismissFraction(float fraction) {
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
+ m.obj = fraction;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify menu to show", e);
+ }
+ } else if (!mStartActivityRequested) {
+ startMenuActivity(null /* stackBounds */, null /* movementBounds */,
+ false /* showMenu */);
+ }
+ }
+
+ /**
* Shows the menu activity.
*/
public void showMenu(Rect stackBounds, Rect movementBounds) {
@@ -191,28 +213,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Could not notify menu to show", e);
}
- } else {
- // Start the menu activity on the top task of the pinned stack
- try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
- pinnedStackInfo.taskIds.length > 0) {
- Intent intent = new Intent(mContext, PipMenuActivity.class);
- intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
- intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
- intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
- intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
- ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
- options.setLaunchTaskId(
- pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
- options.setTaskOverlay(true, true /* canResume */);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- } else {
- Log.e(TAG, "No PIP tasks found");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error showing PIP menu activity", e);
- }
+ } else if (!mStartActivityRequested) {
+ startMenuActivity(stackBounds, movementBounds, true /* showMenu */);
}
}
@@ -272,6 +274,39 @@
}
/**
+ * Starts the menu activity on the top task of the pinned stack.
+ */
+ private void startMenuActivity(Rect stackBounds, Rect movementBounds, boolean showMenu) {
+ try {
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+ pinnedStackInfo.taskIds.length > 0) {
+ Intent intent = new Intent(mContext, PipMenuActivity.class);
+ intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+ intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
+ if (stackBounds != null) {
+ intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
+ }
+ if (movementBounds != null) {
+ intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
+ }
+ intent.putExtra(EXTRA_SHOW_MENU, showMenu);
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+ options.setLaunchTaskId(
+ pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+ options.setTaskOverlay(true, true /* canResume */);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ mStartActivityRequested = true;
+ } else {
+ Log.e(TAG, "No PIP tasks found");
+ }
+ } catch (RemoteException e) {
+ mStartActivityRequested = false;
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
+ }
+
+ /**
* Updates the PiP menu activity with the best set of actions provided.
*/
private void updateMenuActions() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 49d89a2..c4cf28c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -65,9 +65,9 @@
private static final int IME_SHIFT_DURATION = 300;
// The fraction of the stack width that the user has to drag offscreen to minimize the PiP
- private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
- // The fraction of the stack height that the user has to drag offscreen to minimize the PiP
- private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f;
+ private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
+ // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
+ private static final float DISMISS_OFFSCREEN_FRACTION = 0.15f;
private Context mContext;
private IActivityManager mActivityManager;
@@ -234,12 +234,16 @@
/**
* Animates the PiP to the minimized state, slightly offscreen.
*/
- Rect animateToClosestMinimizedState(Rect movementBounds) {
+ Rect animateToClosestMinimizedState(Rect movementBounds,
+ AnimatorUpdateListener updateListener) {
cancelAnimations();
Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
if (!mBounds.equals(toBounds)) {
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
+ if (updateListener != null) {
+ mBoundsAnimator.addUpdateListener(updateListener);
+ }
mBoundsAnimator.start();
}
return toBounds;
@@ -248,7 +252,8 @@
/**
* Flings the PiP to the closest snap target.
*/
- Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds) {
+ Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds,
+ AnimatorUpdateListener listener) {
cancelAnimations();
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
velocityX, velocityY);
@@ -258,6 +263,9 @@
mFlingAnimationUtils.apply(mBoundsAnimator, 0,
distanceBetweenRectOffsets(mBounds, toBounds),
velocity);
+ if (listener != null) {
+ mBoundsAnimator.addUpdateListener(listener);
+ }
mBoundsAnimator.start();
}
return toBounds;
@@ -266,12 +274,15 @@
/**
* Animates the PiP to the closest snap target.
*/
- Rect animateToClosestSnapTarget(Rect movementBounds) {
+ Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) {
cancelAnimations();
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
if (!mBounds.equals(toBounds)) {
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+ if (listener != null) {
+ mBoundsAnimator.addUpdateListener(listener);
+ }
mBoundsAnimator.start();
}
return toBounds;
@@ -316,7 +327,7 @@
/**
* Animates the dismissal of the PiP off the edge of the screen.
*/
- Rect animateDragToEdgeDismiss(Rect pipBounds) {
+ Rect animateDragToEdgeDismiss(Rect pipBounds, AnimatorUpdateListener listener) {
cancelAnimations();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
@@ -330,6 +341,9 @@
dismissPip();
}
});
+ if (listener != null) {
+ mBoundsAnimator.addUpdateListener(listener);
+ }
mBoundsAnimator.start();
return toBounds;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 80231a9..c52fc3e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,8 @@
package com.android.systemui.pip.phone;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.IActivityManager;
import android.content.Context;
import android.graphics.Point;
@@ -55,7 +57,7 @@
// Allow dragging the PIP to a location to close it
private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false;
- private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = false;
+ private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
private final Context mContext;
private final IActivityManager mActivityManager;
@@ -87,6 +89,13 @@
}
}
};
+ private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateDismissFraction();
+ }
+ };
// Behaviour states
private boolean mIsMenuVisible;
@@ -124,7 +133,7 @@
@Override
public void onPipMinimize() {
setMinimizedStateInternal(true);
- mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */);
}
@Override
@@ -331,6 +340,22 @@
}
/**
+ * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
+ */
+ void updateDismissFraction() {
+ if (mMenuController != null) {
+ Rect bounds = mMotionHelper.getBounds();
+ final float target = mMovementBounds.bottom + bounds.height();
+ float fraction = 0f;
+ if (bounds.bottom > target) {
+ final float distance = bounds.bottom - target;
+ fraction = Math.min(distance / bounds.height(), 1f);
+ }
+ mMenuController.setDismissFraction(fraction);
+ }
+ }
+
+ /**
* Sets the controller to update the system of changes from user interaction.
*/
void setPinnedStackController(IPinnedStackController controller) {
@@ -465,6 +490,9 @@
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.updateDismissTarget(mTmpBounds);
}
+ if (ENABLE_DISMISS_DRAG_TO_EDGE) {
+ updateDismissFraction();
+ }
return true;
}
return false;
@@ -502,7 +530,8 @@
boolean isFlingToBot = isFlingTowardsEdge(touchState, 4 /* bottom */);
if (ENABLE_DISMISS_DRAG_TO_EDGE
&& (mMotionHelper.shouldDismissPip() || isFlingToBot)) {
- mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds());
+ mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds(),
+ mUpdateScrimListener);
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
@@ -517,7 +546,8 @@
// minimize offset adjusted
mMenuController.hideMenu();
} else {
- mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
+ mUpdateScrimListener);
}
return true;
}
@@ -536,13 +566,14 @@
final PointF vel = mTouchState.getVelocity();
final float velocity = PointF.length(vel.x, vel.y);
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
+ mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
+ mUpdateScrimListener);
} else {
- mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
+ mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener);
}
} else if (mIsMinimized) {
// This was a tap, so no longer minimized
- mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
+ mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* listener */);
setMinimizedStateInternal(false);
} else if (!mIsMenuVisible) {
mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
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 8a3c4e3..2b52b48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -35,6 +35,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
@@ -46,6 +47,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -104,6 +106,7 @@
private int mNavigationIconHints = 0;
private int mNavigationBarMode;
private AccessibilityManager mAccessibilityManager;
+ private MagnificationContentObserver mMagnificationObserver;
private int mDisabledFlags1;
private StatusBar mStatusBar;
@@ -135,6 +138,12 @@
mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
mAccessibilityManager.addAccessibilityServicesStateChangeListener(
this::updateAccessibilityServicesState);
+ mMagnificationObserver = new MagnificationContentObserver(
+ getContext().getMainThreadHandler());
+ getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
+ mMagnificationObserver);
+
if (savedInstanceState != null) {
mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
}
@@ -154,6 +163,7 @@
mCommandQueue.removeCallbacks(this);
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
this::updateAccessibilityServicesState);
+ getContext().getContentResolver().unregisterContentObserver(mMagnificationObserver);
try {
WindowManagerGlobal.getWindowManagerService()
.removeRotationWatcher(mRotationWatcher);
@@ -387,6 +397,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
+ updateAccessibilityServicesState();
}
private boolean onHomeTouch(View v, MotionEvent event) {
@@ -550,10 +561,18 @@
}
private void updateAccessibilityServicesState() {
+ int requestingServices = 0;
+ try {
+ if (Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED) == 1) {
+ requestingServices++;
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ }
+
final List<AccessibilityServiceInfo> services =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- int requestingServices = 0;
for (int i = services.size() - 1; i >= 0; --i) {
AccessibilityServiceInfo info = services.get(i);
if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
@@ -600,6 +619,18 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
+ private class MagnificationContentObserver extends ContentObserver {
+
+ public MagnificationContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ NavigationBarFragment.this.updateAccessibilityServicesState();
+ }
+ }
+
private final Stub mRotationWatcher = new Stub() {
@Override
public void onRotationChanged(int rotation) throws RemoteException {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 03582ea..3800f29 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3604,6 +3604,10 @@
// ACTION: Settings > Search > Click saved queries
ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY = 881;
+ // OPEN: Settings > Security & screen lock -> Lock screen preferences
+ // CATEGORY: SETTINGS
+ SETTINGS_LOCK_SCREEN_PREFERENCES = 882;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b4630ef..c532efb 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -48,9 +48,6 @@
using namespace android;
-template <typename... T>
-void UNUSED(T... t) {}
-
#define PER_ARRAY_TYPE(flag, fnc, readonly, ...) { \
jint len = 0; \
void *ptr = nullptr; \
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index fd93865..9e4d89c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -77,9 +77,6 @@
*/
static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
- static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
- | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
- | FLAG_FEATURE_SCREEN_MAGNIFIER;
/**
* Flag for enabling the feature to control the screen magnifier. If
* {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
@@ -90,6 +87,16 @@
*/
static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
+ /**
+ * Flag for enabling the feature to trigger the screen magnifier
+ * from another on-device interaction.
+ */
+ static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040;
+
+ static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
+ | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
+ | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
+
private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
@Override
public void run() {
@@ -379,6 +386,12 @@
}
}
+ void notifyAccessibilityButtonClicked() {
+ if (mMagnificationGestureHandler != null) {
+ mMagnificationGestureHandler.notifyShortcutTriggered();
+ }
+ }
+
private void enableFeatures() {
resetStreamState();
@@ -393,11 +406,14 @@
}
if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || (mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+ || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
final boolean detectControlGestures = (mEnabledFeatures
& FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+ final boolean triggerable = (mEnabledFeatures
+ & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
mMagnificationGestureHandler = new MagnificationGestureHandler(
- mContext, mAms, detectControlGestures);
+ mContext, mAms, detectControlGestures, triggerable);
addFirstEventHandler(mMagnificationGestureHandler);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 98ce00e..397938a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -781,6 +781,7 @@
userState.mIsTouchExplorationEnabled = false;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsNavBarMagnificationEnabled = false;
userState.mIsAutoclickEnabled = false;
userState.mEnabledServices.clear();
}
@@ -831,6 +832,7 @@
userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsNavBarMagnificationEnabled = false;
userState.mIsAutoclickEnabled = false;
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
@@ -1152,11 +1154,16 @@
private void notifyAccessibilityButtonClickedLocked() {
final UserState state = getCurrentUserStateLocked();
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final Service service = state.mBoundServices.get(i);
- // TODO(b/34720082): Only notify a single user-defined service
- if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonClickedLocked();
+ if (state.mIsNavBarMagnificationEnabled) {
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget();
+ } else {
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = state.mBoundServices.get(i);
+ // TODO(b/34720082): Only notify a single user-defined service
+ if (service.mRequestAccessibilityButton) {
+ service.notifyAccessibilityButtonClickedLocked();
+ }
}
}
}
@@ -1548,6 +1555,9 @@
if (userState.mIsDisplayMagnificationEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
+ if (userState.mIsNavBarMagnificationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
+ }
if (userHasMagnificationServicesLocked(userState)) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
}
@@ -1781,7 +1791,7 @@
somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
- somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+ somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
return somethingChanged;
@@ -1810,13 +1820,19 @@
return false;
}
- private boolean readDisplayMagnificationEnabledSettingLocked(UserState userState) {
+ private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
0, userState.mUserId) == 1;
- if (displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) {
+ final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ 0, userState.mUserId) == 1;
+ if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
+ || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
+ userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
return true;
}
return false;
@@ -2018,8 +2034,8 @@
return;
}
- if (userState.mIsDisplayMagnificationEnabled ||
- userHasListeningMagnificationServicesLocked(userState)) {
+ if (userState.mIsDisplayMagnificationEnabled || userState.mIsNavBarMagnificationEnabled
+ || userHasListeningMagnificationServicesLocked(userState)) {
// Initialize the magnification controller if necessary
getMagnificationController();
mMagnificationController.register();
@@ -2241,6 +2257,8 @@
pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
pw.append(", displayMagnificationEnabled="
+ userState.mIsDisplayMagnificationEnabled);
+ pw.append(", navBarMagnificationEnabled="
+ + userState.mIsNavBarMagnificationEnabled);
pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
if (userState.mUiAutomationService != null) {
pw.append(", ");
@@ -2320,6 +2338,7 @@
public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10;
public static final int MSG_UPDATE_FINGERPRINT = 11;
public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
+ public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
public MainHandler(Looper looper) {
super(looper);
@@ -2406,6 +2425,14 @@
}
});
} break;
+
+ case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.notifyAccessibilityButtonClicked();
+ }
+ }
+ }
}
}
@@ -4788,6 +4815,7 @@
public boolean mIsTextHighContrastEnabled;
public boolean mIsEnhancedWebAccessibilityEnabled;
public boolean mIsDisplayMagnificationEnabled;
+ public boolean mIsNavBarMagnificationEnabled;
public boolean mIsAutoclickEnabled;
public boolean mIsPerformGesturesEnabled;
public boolean mIsFilterKeyEventsEnabled;
@@ -4856,6 +4884,7 @@
mIsTouchExplorationEnabled = false;
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
+ mIsNavBarMagnificationEnabled = false;
mIsAutoclickEnabled = false;
mSoftKeyboardShowMode = 0;
@@ -4888,6 +4917,9 @@
private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+
private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
@@ -4927,6 +4959,8 @@
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri,
+ false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mAutoclickEnabledUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -4966,8 +5000,9 @@
if (readTouchExplorationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
- if (readDisplayMagnificationEnabledSettingLocked(userState)) {
+ } else if (mDisplayMagnificationEnabledUri.equals(uri)
+ || mNavBarMagnificationEnabledUri.equals(uri)) {
+ if (readMagnificationEnabledSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
} else if (mAutoclickEnabledUri.equals(uri)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index f65046c..caa74b9 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -685,6 +685,12 @@
}
}
+ void setForceShowMagnifiableBounds(boolean show) {
+ if (mRegistered) {
+ mWindowManager.setForceShowMagnifiableBounds(show);
+ }
+ }
+
private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
final float scale = getSentScale();
final float offsetX = getSentOffsetX();
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index f6e5340..7e82eda 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -16,7 +16,10 @@
package com.android.server.accessibility;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.util.MathUtils;
@@ -57,22 +60,30 @@
* be the same but when the finger goes up the screen will stay magnified.
* In other words, the initial magnified state is sticky.
*
- * 3. Pinching with any number of additional fingers when viewport dragging
+ * 3. Magnification can optionally be "triggered" by some external shortcut
+ * affordance. When this occurs via {@link #notifyShortcutTriggered()} a
+ * subsequent tap in a magnifiable region will engage permanent screen
+ * magnification as described in #1. Alternatively, a subsequent long-press
+ * or drag will engage magnification with viewport dragging as described in
+ * #2. Once magnified, all following behaviors apply whether magnification
+ * was engaged via a triple-tap or by a triggered shortcut.
+ *
+ * 4. Pinching with any number of additional fingers when viewport dragging
* is enabled, i.e. the user triple tapped and holds, would adjust the
* magnification scale which will become the current default magnification
* scale. The next time the user magnifies the same magnification scale
* would be used.
*
- * 4. When in a permanent magnified state the user can use two or more fingers
+ * 5. When in a permanent magnified state the user can use two or more fingers
* to pan the viewport. Note that in this mode the content is panned as
* opposed to the viewport dragging mode in which the viewport is moved.
*
- * 5. When in a permanent magnified state the user can use two or more
+ * 6. When in a permanent magnified state the user can use two or more
* fingers to change the magnification scale which will become the current
* default magnification scale. The next time the user magnifies the same
* magnification scale would be used.
*
- * 6. The magnification scale will be persisted in settings and in the cloud.
+ * 7. The magnification scale will be persisted in settings and in the cloud.
*/
class MagnificationGestureHandler implements EventStreamTransformation {
private static final String LOG_TAG = "MagnificationEventHandler";
@@ -94,8 +105,10 @@
private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler;
private final StateViewportDraggingHandler mStateViewportDraggingHandler;
+ private final ScreenStateReceiver mScreenStateReceiver;
- private final boolean mDetectControlGestures;
+ private final boolean mDetectTripleTap;
+ private final boolean mTriggerable;
private EventStreamTransformation mNext;
@@ -104,19 +117,39 @@
private boolean mTranslationEnabledBeforePan;
+ private boolean mShortcutTriggered;
+
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
private long mDelegatingStateDownTime;
+ /**
+ * @param context Context for resolving various magnification-related resources
+ * @param ams AccessibilityManagerService used to obtain a {@link MagnificationController}
+ * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap
+ * gestures for engaging and disengaging magnification,
+ * {@code false} if it should ignore such gestures
+ * @param triggerable {@code true} if this detector should be "triggerable" by some external
+ * shortcut invoking {@link #notifyShortcutTriggered}, {@code
+ * false} if it should ignore such triggers.
+ */
public MagnificationGestureHandler(Context context, AccessibilityManagerService ams,
- boolean detectControlGestures) {
+ boolean detectTripleTap, boolean triggerable) {
mMagnificationController = ams.getMagnificationController();
mDetectingStateHandler = new DetectingStateHandler(context);
mStateViewportDraggingHandler = new StateViewportDraggingHandler();
mMagnifiedContentInteractionStateHandler =
new MagnifiedContentInteractionStateHandler(context);
- mDetectControlGestures = detectControlGestures;
+ mDetectTripleTap = detectTripleTap;
+ mTriggerable = triggerable;
+
+ if (triggerable) {
+ mScreenStateReceiver = new ScreenStateReceiver(context, this);
+ mScreenStateReceiver.register();
+ } else {
+ mScreenStateReceiver = null;
+ }
transitionToState(STATE_DETECTING);
}
@@ -129,7 +162,7 @@
}
return;
}
- if (!mDetectControlGestures) {
+ if (!mDetectTripleTap && !mTriggerable) {
if (mNext != null) {
dispatchTransformedEvent(event, rawEvent, policyFlags);
}
@@ -151,7 +184,7 @@
break;
case STATE_MAGNIFIED_INTERACTION: {
// mMagnifiedContentInteractionStateHandler handles events only
- // if this is the current state since it uses ScaleGestureDetecotr
+ // if this is the current state since it uses ScaleGestureDetector
// and a GestureDetector which need well formed event stream.
}
break;
@@ -193,11 +226,34 @@
@Override
public void onDestroy() {
+ if (mScreenStateReceiver != null) {
+ mScreenStateReceiver.unregister();
+ }
clear();
}
+ void notifyShortcutTriggered() {
+ if (mTriggerable) {
+ if (mMagnificationController.resetIfNeeded(true)) {
+ clear();
+ } else {
+ setMagnificationShortcutTriggered(!mShortcutTriggered);
+ }
+ }
+ }
+
+ private void setMagnificationShortcutTriggered(boolean state) {
+ if (mShortcutTriggered == state) {
+ return;
+ }
+
+ mShortcutTriggered = state;
+ mMagnificationController.setForceShowMagnifiableBounds(state);
+ }
+
private void clear() {
mCurrentState = STATE_DETECTING;
+ setMagnificationShortcutTriggered(false);
mDetectingStateHandler.clear();
mStateViewportDraggingHandler.clear();
mMagnifiedContentInteractionStateHandler.clear();
@@ -575,31 +631,51 @@
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(!mShortcutTriggered);
return;
}
- if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null
- && GestureUtils.isMultiTap(mLastDownEvent, event,
- mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ if (mShortcutTriggered) {
Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
policyFlags, 0, event);
mHandler.sendMessageDelayed(message,
ViewConfiguration.getLongPressTimeout());
- } else if (mTapCount < ACTION_TAP_COUNT) {
+ return;
+ }
+ if (mDetectTripleTap) {
+ if ((mTapCount == ACTION_TAP_COUNT - 1) && (mLastDownEvent != null)
+ && GestureUtils.isMultiTap(mLastDownEvent, event, mMultiTapTimeSlop,
+ mMultiTapDistanceSlop, 0)) {
+ Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
+ policyFlags, 0, event);
+ mHandler.sendMessageDelayed(message,
+ ViewConfiguration.getLongPressTimeout());
+ } else if (mTapCount < ACTION_TAP_COUNT) {
+ Message message = mHandler.obtainMessage(
+ MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
+ }
+ clearLastDownEvent();
+ mLastDownEvent = MotionEvent.obtain(event);
+ } else if (mMagnificationController.isMagnifying()) {
+ // If magnified, consume an ACTION_DOWN until mMultiTapTimeSlop or
+ // mTapDistanceSlop is reached to ensure MAGNIFIED_INTERACTION is reachable.
Message message = mHandler.obtainMessage(
MESSAGE_TRANSITION_TO_DELEGATING_STATE);
mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
+ return;
+ } else {
+ transitionToDelegatingState(true);
+ return;
}
- clearLastDownEvent();
- mLastDownEvent = MotionEvent.obtain(event);
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
if (mMagnificationController.isMagnifying()) {
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
transitionToState(STATE_MAGNIFIED_INTERACTION);
clear();
} else {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(true);
}
}
break;
@@ -608,29 +684,34 @@
final double distance = GestureUtils.computeDistance(mLastDownEvent,
event, 0);
if (Math.abs(distance) > mTapDistanceSlop) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(true);
}
}
}
break;
case MotionEvent.ACTION_UP: {
+ if (!mMagnificationController.magnificationRegionContains(
+ event.getX(), event.getY())) {
+ transitionToDelegatingState(!mShortcutTriggered);
+ return;
+ }
+ if (mShortcutTriggered) {
+ clear();
+ onActionTap(event, policyFlags);
+ return;
+ }
if (mLastDownEvent == null) {
return;
}
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
- if (!mMagnificationController.magnificationRegionContains(
- event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
- return;
- }
if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
mTapDistanceSlop, 0)) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(true);
return;
}
- if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent,
- event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
- transitionToDelegatingStateAndClear();
+ if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(
+ mLastTapUpEvent, event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ transitionToDelegatingState(true);
return;
}
mTapCount++;
@@ -655,6 +736,7 @@
@Override
public void clear() {
+ setMagnificationShortcutTriggered(false);
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
clearTapDetectionState();
@@ -714,10 +796,12 @@
}
}
- private void transitionToDelegatingStateAndClear() {
+ private void transitionToDelegatingState(boolean andClear) {
transitionToState(STATE_DELEGATING);
sendDelayedMotionEvents();
- clear();
+ if (andClear) {
+ clear();
+ }
}
private void onActionTap(MotionEvent up, int policyFlags) {
@@ -820,4 +904,30 @@
mPolicyFlags = 0;
}
}
+
+ /**
+ * BroadcastReceiver used to cancel the magnification shortcut when the screen turns off
+ */
+ private static class ScreenStateReceiver extends BroadcastReceiver {
+ private final Context mContext;
+ private final MagnificationGestureHandler mGestureHandler;
+
+ public ScreenStateReceiver(Context context, MagnificationGestureHandler gestureHandler) {
+ mContext = context;
+ mGestureHandler = gestureHandler;
+ }
+
+ public void register() {
+ mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ }
+
+ public void unregister() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mGestureHandler.setMagnificationShortcutTriggered(false);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 75916be..2885e66 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1584,9 +1584,9 @@
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
- if (topStack.isAssistantStack()
- && topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) {
- return STACK_VISIBLE;
+ if (topStack.isAssistantStack()) {
+ return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
+ : STACK_INVISIBLE;
}
// Otherwise, the docked stack is always visible, except in the case where the top
@@ -3216,7 +3216,8 @@
}
// Move the home stack to the top if this stack is fullscreen or there is no
// other visible stack.
- if (mStackSupervisor.moveHomeStackTaskToTop(myReason)) {
+ if (task.isOverHomeStack() &&
+ mStackSupervisor.moveHomeStackTaskToTop(myReason)) {
// Activity focus was already adjusted. Nothing else to do...
return;
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 71bfa64..e1426fd 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -245,9 +245,12 @@
// STOPSHIP Remove the whitelist.
if ("com.google.android.talk".equals(callingPackage)
- || "com.google.android.quicksearchbox".equals(callingPackage)) {
+ || "com.google.android.quicksearchbox".equals(callingPackage)
+ || "com.google.android.googlequicksearchbox".equals(callingPackage)
+ ) {
return false;
}
+ // STOPSHIP Change it to 'e'.
Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile "
+ targetUserId + " from " + callingUserId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1289019..4ac1cce 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10565,9 +10565,9 @@
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ " (requirer="
- + (requirer == null ? "null" : requirer.pkg.packageName)
+ + (requirer != null ? requirer.pkg : "null")
+ ", scannedPackage="
- + (scannedPackage != null ? scannedPackage.packageName : "null")
+ + (scannedPackage != null ? scannedPackage : "null")
+ ")");
try {
mInstaller.rmdex(ps.codePathString,
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1510dd1..5abc4e4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -222,6 +222,14 @@
|| mWindowsForAccessibilityObserver != null);
}
+ /** NOTE: This has to be called within a surface transaction. */
+ public void setForceShowMagnifiableBoundsLocked(boolean show) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+ mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ }
+ }
+
private static void populateTransformationMatrixLocked(WindowState windowState,
Matrix outMatrix) {
sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
@@ -266,6 +274,8 @@
private final long mLongAnimationDuration;
+ private boolean mForceShowMagnifiableBounds = false;
+
public DisplayMagnifier(WindowManagerService windowManagerService,
MagnificationCallbacks callbacks) {
mContext = windowManagerService.mContext;
@@ -283,6 +293,15 @@
mWindowManagerService.scheduleAnimationLocked();
}
+ public void setForceShowMagnifiableBoundsLocked(boolean show) {
+ mForceShowMagnifiableBounds = show;
+ mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
+ }
+
+ public boolean isForceShowingMagnifiableBoundsLocked() {
+ return mForceShowMagnifiableBounds;
+ }
+
public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -488,7 +507,8 @@
// to show the border. We will do so when the pending message is handled.
if (!mHandler.hasMessages(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+ setMagnifiedRegionBorderShownLocked(
+ isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
}
}
@@ -600,11 +620,11 @@
}
public void onRotationChangedLocked() {
- // If we are magnifying, hide the magnified border window immediately so
+ // If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
- // used for rotation has already the border. After the rotation is complete
+ // used for rotation already has the border. After the rotation is complete
// we will show the border.
- if (isMagnifyingLocked()) {
+ if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
setMagnifiedRegionBorderShownLocked(false, false);
final long delay = (long) (mLongAnimationDuration
* mWindowManagerService.getWindowAnimationScaleLocked());
@@ -926,7 +946,8 @@
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mWindowManagerService.mWindowMap) {
- if (mMagnifedViewport.isMagnifyingLocked()) {
+ if (mMagnifedViewport.isMagnifyingLocked()
+ || isForceShowingMagnifiableBoundsLocked()) {
mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
mWindowManagerService.scheduleAnimationLocked();
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 75a7385..7a36da2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -44,6 +44,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
@@ -1652,10 +1653,14 @@
}
int getAppStackClipMode() {
+ // When dismiss keyguard animation occurs, clip before the animation to prevent docked
+ // app from showing beyond the divider
+ if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
+ || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+ return STACK_CLIP_BEFORE_ANIM;
+ }
return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
|| mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
- || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
- || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
|| mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
? STACK_CLIP_NONE
: STACK_CLIP_AFTER_ANIM;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b063e01..014a89d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7158,6 +7158,17 @@
}
@Override
+ public void setForceShowMagnifiableBounds(boolean show) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController != null) {
+ mAccessibilityController.setForceShowMagnifiableBoundsLocked(show);
+ } else {
+ throw new IllegalStateException("Magnification callbacks not set!");
+ }
+ }
+ }
+
+ @Override
public void getMagnificationRegion(@NonNull Region magnificationRegion) {
synchronized (mWindowMap) {
if (mAccessibilityController != null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 51b91f4..7862523 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5276,23 +5276,42 @@
}
}
+
+ /**
+ * @deprecated use {@link #isDataEnabled()} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ public boolean getDataEnabled() {
+ return isDataEnabled();
+ }
+
/**
* Returns whether mobile data is enabled or not.
*
- * <p>Requires Permission:
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
- * calling app has carrier privileges.
+ * <p>Requires one of the following permissions:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+ * calling app has carrier privileges.
+ *
+ * <p>Note that this does not take into account any data restrictions that may be present on the
+ * calling app. Such restrictions may be inspected with
+ * {@link ConnectivityManager#getRestrictBackgroundStatus}.
*
* @return true if mobile data is enabled.
*
* @see #hasCarrierPrivileges
*/
- public boolean getDataEnabled() {
+ @SuppressWarnings("deprecation")
+ public boolean isDataEnabled() {
return getDataEnabled(getSubId());
}
- /** @hide */
+ /**
+ * @deprecated use {@link #isDataEnabled(int)} instead.
+ * @hide
+ */
@SystemApi
public boolean getDataEnabled(int subId) {
boolean retVal = false;
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 58511b9..3691cac 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -315,14 +315,15 @@
}
static ConnectivityMetricsEvent expectedEvent(int timestamp) {
- return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV);
+ ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+ ev.timestamp = timestamp;
+ ev.data = FAKE_EV;
+ return ev;
}
/** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
assertEquals(expected.timestamp, got.timestamp);
- assertEquals(expected.componentTag, got.componentTag);
- assertEquals(expected.eventTag, got.eventTag);
assertEquals(expected.data, got.data);
}
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
index c5965e8..5064b9b 100644
--- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
+++ b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
@@ -28,7 +28,10 @@
}
static ConnectivityMetricsEvent ev(Parcelable p) {
- return new ConnectivityMetricsEvent(1L, 0, 0, p);
+ ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+ ev.timestamp = 1L;
+ ev.data = p;
+ return ev;
}
static ConnectivityMetricsEvent describeIpEvent(Consumer<Parcel>... fs) {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 18c1245..af48d0a 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,11 @@
package android.net.wifi;
+
+import android.content.pm.ParceledListSlice;
+
import android.net.wifi.hotspot2.PasspointConfiguration;
+
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.ScanSettings;
@@ -51,9 +55,9 @@
*/
oneway void requestActivityInfo(in ResultReceiver result);
- List<WifiConfiguration> getConfiguredNetworks();
+ ParceledListSlice getConfiguredNetworks();
- List<WifiConfiguration> getPrivilegedConfiguredNetworks();
+ ParceledListSlice getPrivilegedConfiguredNetworks();
WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 447cafb..4f2881b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.Network;
@@ -46,6 +47,7 @@
import java.net.InetAddress;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.Collections;
/**
* This class provides the primary API for managing all aspects of Wi-Fi
@@ -811,7 +813,12 @@
*/
public List<WifiConfiguration> getConfiguredNetworks() {
try {
- return mService.getConfiguredNetworks();
+ ParceledListSlice<WifiConfiguration> parceledList =
+ mService.getConfiguredNetworks();
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -821,7 +828,12 @@
@SystemApi
public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
try {
- return mService.getPrivilegedConfiguredNetworks();
+ ParceledListSlice<WifiConfiguration> parceledList =
+ mService.getPrivilegedConfiguredNetworks();
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}