diff options
52 files changed, 1495 insertions, 840 deletions
diff --git a/api/current.txt b/api/current.txt index 5b55fdc4b966..8a25ab4e5b71 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12599,7 +12599,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -12852,8 +12851,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -12994,8 +12991,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13068,15 +13063,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -13586,10 +13577,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -13599,8 +13586,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -13785,8 +13770,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -22774,7 +22757,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); 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.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22788,7 +22771,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -22815,7 +22798,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -22856,6 +22839,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -22865,7 +22852,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -22897,7 +22883,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -22906,7 +22892,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -22937,8 +22923,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -26751,8 +26741,8 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -26838,8 +26828,8 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -37122,6 +37112,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -37134,9 +37125,9 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); - method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean); } public final class SaveRequest implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 23a8cad152da..874917964717 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13373,7 +13373,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -13626,8 +13625,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -13768,8 +13765,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13842,15 +13837,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -14360,10 +14351,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -14373,8 +14360,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -14559,8 +14544,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -24610,7 +24593,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); 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.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -24624,7 +24607,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -24651,7 +24634,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -24692,6 +24675,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -24701,7 +24688,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -24733,7 +24719,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -24742,7 +24728,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -24773,8 +24759,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -29494,9 +29484,9 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); - method public java.lang.String createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -29582,9 +29572,9 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); - method public java.lang.String createNetworkSpecifierPmk(int, byte[], byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, byte[], byte[]); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -40234,6 +40224,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -40246,9 +40237,9 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); - method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean); } public final class SaveRequest implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 3373ab735a36..7a73d4b62f77 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12641,7 +12641,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -12894,8 +12893,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -13036,8 +13033,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13110,15 +13105,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -13628,10 +13619,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -13641,8 +13628,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -13827,8 +13812,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -22881,7 +22864,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); 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.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22895,7 +22878,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -22922,7 +22905,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -22963,6 +22946,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -22972,7 +22959,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -23004,7 +22990,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -23013,7 +22999,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -23044,8 +23030,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -26858,8 +26848,8 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -26945,8 +26935,8 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -37275,6 +37265,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -37287,9 +37278,9 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); - method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean); } public final class SaveRequest implements android.os.Parcelable { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 09906be7cd26..178b9671fc27 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -54,6 +54,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.provider.Settings; import android.util.AndroidException; import android.util.Log; @@ -3983,6 +3984,8 @@ public abstract class PackageManager { * <p>If no packages have been changed, returns <code>null</code>. * <p>The sequence number starts at <code>0</code> and is * reset every boot. + * @param sequenceNumber The first sequence number for which to retrieve package changes. + * @see Settings.Global#BOOT_COUNT */ public abstract @Nullable ChangedPackages getChangedPackages( @IntRange(from=0) int sequenceNumber); @@ -6256,18 +6259,18 @@ public abstract class PackageManager { /** * Checks whether the calling package is allowed to request package installs through package - * installer. Apps are encouraged to call this api before launching the package installer via + * installer. Apps are encouraged to call this API before launching the package installer via * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the * user can explicitly choose what external sources they trust to install apps on the device. - * If this api returns false, the install request will be blocked by the package installer and + * If this API returns false, the install request will be blocked by the package installer and * a dialog will be shown to the user with an option to launch settings to change their * preference. An application must target Android O or higher and declare permission - * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api. + * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this API. * * @return true if the calling package is trusted by the user to request install packages on * the device, false otherwise. - * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE} - * @see {@link android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES} + * @see android.content.Intent#ACTION_INSTALL_PACKAGE + * @see android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES */ public abstract boolean canRequestPackageInstalls(); diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java index a76a8a0e3da2..3c6baa76558a 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -54,7 +54,11 @@ public class LogMaker { /* Deserialize from the eventlog */ public LogMaker(Object[] items) { - deserialize(items); + if (items != null) { + deserialize(items); + } else { + setCategory(MetricsEvent.VIEW_UNKNOWN); + } } /** @param category to replace the existing setting. */ @@ -373,13 +377,13 @@ public class LogMaker { */ public void deserialize(Object[] items) { int i = 0; - while (i < items.length) { + while (items != null && i < items.length) { Object key = items[i++]; Object value = i < items.length ? items[i++] : null; if (key instanceof Integer) { entries.put((Integer) key, value); } else { - Log.i(TAG, "Invalid key " + key.toString()); + Log.i(TAG, "Invalid key " + (key == null ? "null" : key.toString())); } } } diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index d3adce73582e..7496cb28f046 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -135,6 +135,7 @@ public class Preference implements Comparable<Preference> { private boolean mDependencyMet = true; private boolean mParentDependencyMet = true; private boolean mRecycleEnabled = true; + private boolean mHasSingleLineTitleAttr; private boolean mSingleLineTitle = true; private boolean mIconSpaceReserved; @@ -303,6 +304,7 @@ public class Preference implements Comparable<Preference> { case com.android.internal.R.styleable.Preference_singleLineTitle: mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle); + mHasSingleLineTitleAttr = true; break; case com.android.internal.R.styleable.Preference_iconSpaceReserved: @@ -609,7 +611,9 @@ public class Preference implements Comparable<Preference> { if (!TextUtils.isEmpty(title)) { titleView.setText(title); titleView.setVisibility(View.VISIBLE); - titleView.setSingleLine(mSingleLineTitle); + if (mHasSingleLineTitleAttr) { + titleView.setSingleLine(mSingleLineTitle); + } } else { titleView.setVisibility(View.GONE); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c6ea9586374d..f32f163a86b0 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7077,6 +7077,7 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED); CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION); CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS); + CLONE_TO_MANAGED_PROFILE.add(AUTOFILL_SERVICE); CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD); CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES); CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS); diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 7f960dff0bdf..915d4f792a31 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -153,13 +153,27 @@ public final class SaveInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface SaveDataType{} + /** + * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)} + * is called once the activity finishes. If this flag is set it is called once all saved views + * become invisible. + */ + public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1; + + /** @hide */ + @IntDef( + flag = true, + value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}) + @Retention(RetentionPolicy.SOURCE) + @interface SaveInfoFlags{} + private final @SaveDataType int mType; private final CharSequence mNegativeActionTitle; private final IntentSender mNegativeActionListener; private final AutofillId[] mRequiredIds; private final AutofillId[] mOptionalIds; private final CharSequence mDescription; - private final boolean mSaveOnAllViewsInvisible; + private final int mFlags; private SaveInfo(Builder builder) { mType = builder.mType; @@ -168,7 +182,7 @@ public final class SaveInfo implements Parcelable { mRequiredIds = builder.mRequiredIds; mOptionalIds = builder.mOptionalIds; mDescription = builder.mDescription; - mSaveOnAllViewsInvisible = builder.mSaveOnAllViewsInvisible; + mFlags = builder.mFlags; } /** @hide */ @@ -197,8 +211,8 @@ public final class SaveInfo implements Parcelable { } /** @hide */ - public boolean saveOnAllViewsInvisible() { - return mSaveOnAllViewsInvisible; + public @SaveInfoFlags int getFlags() { + return mFlags; } /** @hide */ @@ -219,7 +233,7 @@ public final class SaveInfo implements Parcelable { private AutofillId[] mOptionalIds; private CharSequence mDescription; private boolean mDestroyed; - private boolean mSaveOnAllViewsInvisible; + private int mFlags; /** * Creates a new builder. @@ -268,17 +282,15 @@ public final class SaveInfo implements Parcelable { } /** - * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)} - * is called once the activity finishes. If this property is set it is called once all - * autofillable or saved views become invisible. + * Set flags changing the save behavior. * - * @param saveOnAllViewsInvisible Set to {@code true} if the data should be saved once - * all the views become invisible. + * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0. * @return This builder. */ - public @NonNull Builder setSaveOnAllViewsInvisible(boolean saveOnAllViewsInvisible) { + public @NonNull Builder setFlags(@SaveInfoFlags int flags) { throwIfDestroyed(); - mSaveOnAllViewsInvisible = saveOnAllViewsInvisible; + + mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE); return this; } @@ -378,7 +390,7 @@ public final class SaveInfo implements Parcelable { .append(", requiredIds=").append(Arrays.toString(mRequiredIds)) .append(", optionalIds=").append(Arrays.toString(mOptionalIds)) .append(", description=").append(mDescription) - .append(", saveOnNoVisibleTrackedViews=").append(mSaveOnAllViewsInvisible) + .append(", mFlags=").append(mFlags) .append("]").toString(); } @@ -399,7 +411,7 @@ public final class SaveInfo implements Parcelable { parcel.writeParcelable(mNegativeActionListener, flags); parcel.writeParcelableArray(mOptionalIds, flags); parcel.writeCharSequence(mDescription); - parcel.writeBoolean(mSaveOnAllViewsInvisible); + parcel.writeInt(mFlags); } public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() { @@ -413,7 +425,7 @@ public final class SaveInfo implements Parcelable { builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null)); builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class)); builder.setDescription(parcel.readCharSequence()); - builder.setSaveOnAllViewsInvisible(parcel.readBoolean()); + builder.setFlags(parcel.readInt()); return builder.build(); } diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index f47c35580f0f..1ccf16a64ade 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -127,20 +127,23 @@ public class FocusFinder { if (focused == null || focused == root) { return root; } - ViewParent effective = focused.getParent(); + ViewGroup effective = null; + ViewParent nextParent = focused.getParent(); do { - if (effective == root) { - return root; + if (nextParent == root) { + return effective != null ? effective : root; } - ViewGroup vg = (ViewGroup) effective; + ViewGroup vg = (ViewGroup) nextParent; if (vg.getTouchscreenBlocksFocus() && focused.getContext().getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN) && vg.isKeyboardNavigationCluster()) { - return vg; + // Don't stop and return here because the cluster could be nested and we only + // care about the top-most one. + effective = vg; } - effective = effective.getParent(); - } while (effective != null); + nextParent = nextParent.getParent(); + } while (nextParent instanceof ViewGroup); return root; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 7d2d77e251a9..928e365962d9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9768,6 +9768,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Searches up the view hierarchy to find the top-most cluster. All deeper/nested clusters + * will be ignored. + * + * @return the keyboard navigation cluster that this view is in (can be this view) + * or {@code null} if not in one + */ + View findKeyboardNavigationCluster() { + if (mParent instanceof View) { + View cluster = ((View) mParent).findKeyboardNavigationCluster(); + if (cluster != null) { + return cluster; + } else if (isKeyboardNavigationCluster()) { + return this; + } + } + return null; + } + + /** * Set whether this view is a root of a keyboard navigation cluster. * * @param isCluster If true, this view is a root of a cluster. @@ -9789,9 +9808,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - public void setFocusedInCluster() { - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).setFocusInCluster(this); + public final void setFocusedInCluster() { + View top = findKeyboardNavigationCluster(); + if (top == this) { + return; + } + ViewParent parent = mParent; + View child = this; + while (parent instanceof ViewGroup) { + ((ViewGroup) parent).setFocusedInCluster(child); + if (parent == top) { + return; + } + child = (View) parent; + parent = parent.getParent(); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f9eb25da642a..d400011391fd 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -807,33 +807,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return mDefaultFocus != null || super.hasDefaultFocus(); } - void setFocusInCluster(View child) { - // Stop at the root of the cluster - if (child.isKeyboardNavigationCluster()) { - return; - } - + void setFocusedInCluster(View child) { mFocusedInCluster = child; - - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).setFocusInCluster(this); - } } - void clearFocusInCluster(View child) { + /** + * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing + * it. + * <br> + * This is intended to be run on {@code child}'s immediate parent. This is necessary because + * the chain is sometimes cleared after {@code child} has been detached. + */ + void clearFocusedInCluster(View child) { if (mFocusedInCluster != child) { return; } - - if (child.isKeyboardNavigationCluster()) { - return; - } - - mFocusedInCluster = null; - - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).clearFocusInCluster(this); - } + View top = findKeyboardNavigationCluster(); + ViewParent parent = this; + do { + ((ViewGroup) parent).mFocusedInCluster = null; + parent = parent.getParent(); + } while (parent != top && parent instanceof ViewGroup); } @Override @@ -1281,7 +1275,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) { if (touchscreenBlocksFocus) { mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS; - if (hasFocus()) { + if (hasFocus() && !isKeyboardNavigationCluster()) { final View focusedChild = getDeepestFocusedChild(); if (!focusedChild.isFocusableInTouchMode()) { final View newFocus = focusSearch(FOCUS_FORWARD); @@ -1316,7 +1310,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // cluster, focus is free to move around within it. return getTouchscreenBlocksFocus() && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) - && (!hasFocus() || !isKeyboardNavigationCluster()); + && !(isKeyboardNavigationCluster() + && (hasFocus() || (findKeyboardNavigationCluster() != this))); } @Override @@ -3217,8 +3212,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) { - if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster() - && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS + if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS && (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE && mFocusedInCluster.restoreFocusInCluster(direction)) { return true; @@ -5182,7 +5176,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } if (view == mFocusedInCluster) { - clearFocusInCluster(view); + clearFocusedInCluster(view); } view.clearAccessibilityFocus(); @@ -5302,7 +5296,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearDefaultFocus = view; } if (view == mFocusedInCluster) { - clearFocusInCluster(view); + clearFocusedInCluster(view); } view.clearAccessibilityFocus(); @@ -5458,7 +5452,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearDefaultFocus(child); } if (child == mFocusedInCluster) { - clearFocusInCluster(child); + clearFocusedInCluster(child); } child.clearAccessibilityFocus(); diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 032c7750c1d6..b9ed96395130 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -34,6 +34,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.widget.ResolverDrawerLayout; import java.util.ArrayList; import java.util.Collections; @@ -56,6 +57,11 @@ public class AccessibilityButtonChooserActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.accessibility_button_chooser); + final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel); + if (rdl != null) { + rdl.setOnDismissedListener(this::finish); + } + String component = Settings.Secure.getString(getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); if (TextUtils.isEmpty(component)) { diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index cc3f58cb0c3d..e28079fd5cdd 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -70,6 +70,7 @@ class WebViewZygoteInit { @Override protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { + Log.i(TAG, "Beginning package preload"); // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that // our children will reuse the same classloader instead of creating their own. // This enables us to preload Java and native code in the webview zygote process and @@ -97,6 +98,7 @@ class WebViewZygoteInit { IllegalAccessException | InvocationTargetException e) { Log.e(TAG, "Exception while preloading package", e); } + Log.i(TAG, "Package preload done"); return false; } } diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml index 0ef785f102bb..480defbd5935 100644 --- a/core/res/res/layout/accessibility_button_chooser.xml +++ b/core/res/res/layout/accessibility_button_chooser.xml @@ -19,7 +19,7 @@ <com.android.internal.widget.ResolverDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:maxWidth="@dimen/resolver_max_width" android:maxCollapsedHeight="256dp" android:maxCollapsedHeightSmall="56dp" @@ -27,11 +27,14 @@ <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_alwaysShow="true" android:orientation="vertical" android:background="?attr/colorBackground" android:paddingTop="8dp" - android:paddingBottom="8dp"> + android:paddingBottom="8dp" + android:paddingStart="?attr/dialogPreferredPadding" + android:paddingEnd="?attr/dialogPreferredPadding"> <TextView android:layout_width="match_parent" @@ -41,8 +44,6 @@ android:text="@string/accessibility_button_prompt_text" android:gravity="start|center_vertical" android:layout_alignParentStart="true" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:paddingTop="8dp" android:paddingBottom="8dp"/> @@ -55,20 +56,15 @@ android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:stretchMode="columnWidth" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:gravity="center"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/accessibility_button_prompt" - android:layout_alwaysShow="true" android:textAppearance="?attr/textAppearanceMedium" android:text="@string/accessibility_button_instructional_text" android:gravity="start|center_vertical" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:paddingTop="8dp" android:paddingBottom="8dp" android:visibility="gone"/> diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java index bab9f631d7d4..63c1f87178aa 100644 --- a/core/tests/coretests/src/android/metrics/LogMakerTest.java +++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java @@ -263,4 +263,32 @@ public class LogMakerTest extends TestCase { assertFalse(a.isSubsetOf(b)); assertFalse(b.isSubsetOf(a)); } + + public void testConstructFromNull() { + new LogMaker(null); + // no promises, just don't throw + } + + public void testConstructFromNullKey() { + Object[] items = new Object[2]; + items[0] = null; + items[1] = "foo"; + new LogMaker(items); + // no promises, just don't throw + } + + public void testConstructFromNullField() { + Object[] items = new Object[2]; + items[0] = 10; + items[1] = null; + new LogMaker(items); + // no promises, just don't throw + } + + public void testConstructFromTruncatedArray() { + Object[] items = new Object[1]; + items[0] = 10; + new LogMaker(items); + // no promises, just don't throw + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java new file mode 100644 index 000000000000..0acff9bcc305 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +import android.os.LocaleList; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.textclassifier.TextClassificationManager; +import android.view.textclassifier.TextClassificationResult; +import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextLanguage; +import android.view.textclassifier.TextSelection; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.Locale; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TextClassificationManagerTest { + + private static final LocaleList LOCALES = LocaleList.forLanguageTags("en"); + + private TextClassificationManager mTcm; + private TextClassifier mClassifier; + + @Before + public void setup() { + mTcm = InstrumentationRegistry.getTargetContext() + .getSystemService(TextClassificationManager.class); + mTcm.setTextClassifier(null); + mClassifier = mTcm.getTextClassifier(); + } + + @Test + public void testSmartSelection() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String selected = "droid"; + String suggested = "droid@android.com"; + int startIndex = text.indexOf(selected); + int endIndex = startIndex + selected.length(); + int smartStartIndex = text.indexOf(suggested); + int smartEndIndex = smartStartIndex + suggested.length(); + + assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testSmartSelection_url() { + if (isTextClassifierDisabled()) return; + + String text = "Visit http://www.android.com for more information"; + String selected = "http"; + String suggested = "http://www.android.com"; + int startIndex = text.indexOf(selected); + int endIndex = startIndex + selected.length(); + int smartStartIndex = text.indexOf(suggested); + int smartEndIndex = smartStartIndex + suggested.length(); + + assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL)); + } + + @Test + public void testTextClassificationResult() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String classifiedText = "droid@android.com"; + int startIndex = text.indexOf(classifiedText); + int endIndex = startIndex + classifiedText.length(); + assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES), + isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testTextClassificationResult_url() { + if (isTextClassifierDisabled()) return; + + String text = "Visit http://www.android.com for more information"; + String classifiedText = "http://www.android.com"; + int startIndex = text.indexOf(classifiedText); + int endIndex = startIndex + classifiedText.length(); + assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES), + isTextClassificationResult(classifiedText, TextClassifier.TYPE_URL)); + } + + @Test + public void testLanguageDetection() { + if (isTextClassifierDisabled()) return; + + String text = "This is a piece of English text"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en")); + + text = "Das ist ein deutscher Text"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de")); + + text = "これは日本語のテキストです"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja")); + } + + @Test + public void testSetTextClassifier() { + TextClassifier classifier = mock(TextClassifier.class); + mTcm.setTextClassifier(classifier); + assertEquals(classifier, mTcm.getTextClassifier()); + } + + private boolean isTextClassifierDisabled() { + return mClassifier == TextClassifier.NO_OP; + } + + private static Matcher<TextSelection> isTextSelection( + final int startIndex, final int endIndex, final String type) { + return new BaseMatcher<TextSelection>() { + @Override + public boolean matches(Object o) { + if (o instanceof TextSelection) { + TextSelection selection = (TextSelection) o; + return startIndex == selection.getSelectionStartIndex() + && endIndex == selection.getSelectionEndIndex() + && selection.getEntityCount() > 0 + && type.equals(selection.getEntity(0)); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendValue( + String.format("%d, %d, %s", startIndex, endIndex, type)); + } + }; + } + + private static Matcher<TextClassificationResult> isTextClassificationResult( + final String text, final String type) { + return new BaseMatcher<TextClassificationResult>() { + @Override + public boolean matches(Object o) { + if (o instanceof TextClassificationResult) { + TextClassificationResult result = (TextClassificationResult) o; + return text.equals(result.getText()) + && result.getEntityCount() > 0 + && type.equals(result.getEntity(0)); + // TODO: Include other properties. + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("text=").appendValue(text) + .appendText(", type=").appendValue(type); + } + }; + } + + private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) { + return new BaseMatcher<List<TextLanguage>>() { + @Override + public boolean matches(Object o) { + if (o instanceof List) { + List languages = (List) o; + if (!languages.isEmpty()) { + Object o1 = languages.get(0); + if (o1 instanceof TextLanguage) { + TextLanguage lang = (TextLanguage) o1; + return lang.getLanguageCount() > 0 + && new Locale(language).getLanguage() + .equals(lang.getLanguage(0).getLanguage()); + } + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendValue(String.format("%s", language)); + } + }; + } +} diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 7a8e4873a7c9..599406cd9e1c 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -916,17 +916,20 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If null or if the config is not - * {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, + * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the + * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} + * is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, - * or if the specified color space's transfer function is not an - * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve} + * if the specified color space's transfer function is not an + * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if + * the color space is null */ public static Bitmap createBitmap(int width, int height, @NonNull Config config, - boolean hasAlpha, @Nullable ColorSpace colorSpace) { + boolean hasAlpha, @NonNull ColorSpace colorSpace) { return createBitmap(null, width, height, config, hasAlpha, colorSpace); } @@ -950,7 +953,8 @@ public final class Bitmap implements Parcelable { */ public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha) { - return createBitmap(display, width, height, config, hasAlpha, null); + return createBitmap(display, width, height, config, hasAlpha, + ColorSpace.get(ColorSpace.Named.SRGB)); } /** @@ -967,26 +971,32 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If null or if the config is not - * {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, + * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the + * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} + * is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, - * or if the specified color space's transfer function is not an - * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve} + * if the specified color space's transfer function is not an + * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if + * the color space is null */ public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, - @NonNull Config config, boolean hasAlpha, @Nullable ColorSpace colorSpace) { + @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); } if (config == Config.HARDWARE) { throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE"); } + if (colorSpace == null) { + throw new IllegalArgumentException("can't create bitmap without a color space"); + } Bitmap bm; - if (colorSpace == null || config != Config.ARGB_8888) { + if (config != Config.ARGB_8888) { bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null); } else { if (!(colorSpace instanceof ColorSpace.Rgb)) { diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index 7e6756ebfdba..5577f53ee28b 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -41,35 +41,16 @@ public class BitmapShader extends Shader { * @param tileY The tiling mode for y to draw the bitmap in. */ public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) { - set(bitmap, tileX, tileY); + this(bitmap, tileX.nativeInt, tileY.nativeInt); } private BitmapShader(Bitmap bitmap, int tileX, int tileY) { - setInternal(bitmap, tileX, tileY); - } - - /** - * Reinitialize the BitmapShader's Bitmap and tile modes. - * - * @param bitmap The bitmap to use inside the shader - * @param tileX The tiling mode for x to draw the bitmap in. - * @param tileY The tiling mode for y to draw the bitmap in. - */ - public void set(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) { - if (tileX == null || tileY == null) { - throw new IllegalArgumentException(); - } - setInternal(bitmap, tileX.nativeInt, tileY.nativeInt); - } - - private void setInternal(Bitmap bitmap, int tileX, int tileY) { if (bitmap == null) { throw new IllegalArgumentException("Bitmap must be non-null"); } if (bitmap == mBitmap && tileX == mTileX && tileY == mTileY) { return; } - discardNativeInstance(); mBitmap = bitmap; mTileX = tileX; mTileY = tileY; diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java index 61f6cc5ba4ee..9201a2e2310e 100644 --- a/graphics/java/android/graphics/ColorMatrixColorFilter.java +++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java @@ -73,6 +73,8 @@ public class ColorMatrixColorFilter extends ColorFilter { * @see #getColorMatrix(ColorMatrix) * @see #setColorMatrixArray(float[]) * @see ColorMatrix#reset() + * + * @hide */ public void setColorMatrix(@Nullable ColorMatrix matrix) { discardNativeInstance(); @@ -99,6 +101,8 @@ public class ColorMatrixColorFilter extends ColorFilter { * * @throws ArrayIndexOutOfBoundsException if the specified array's * length is < 20 + * + * @hide */ public void setColorMatrixArray(@Nullable float[] array) { // called '...Array' so that passing null isn't ambiguous diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java index 8438bf2cc6b4..e107ea724377 100644 --- a/graphics/java/android/graphics/ComposeShader.java +++ b/graphics/java/android/graphics/ComposeShader.java @@ -59,43 +59,10 @@ public class ComposeShader extends Shader { } private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) { - setInternal(shaderA, shaderB, nativeMode); - } - - /** - * Reinitialize the ComposeShader's component Shaders and blend mode. - * - * @param shaderA The colors from this shader are seen as the "dst" by the mode - * @param shaderB The colors from this shader are seen as the "src" by the mode - * @param mode The PorterDuff mode that combines the colors from the two shaders. - */ - public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) { - setInternal(shaderA, shaderB, mode.porterDuffMode); - } - - /** - * Reinitialize the ComposeShader's component Shaders and blend mode. - * - * @param shaderA The colors from this shader are seen as the "dst" by the mode - * @param shaderB The colors from this shader are seen as the "src" by the mode - * @param mode The PorterDuff mode that combines the colors from the two shaders. - */ - public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, - @NonNull PorterDuff.Mode mode) { - setInternal(shaderA, shaderB, mode.nativeInt); - } - - private void setInternal(Shader shaderA, Shader shaderB, int nativeMode) { if (shaderA == null || shaderB == null) { throw new IllegalArgumentException("Shader parameters must not be null"); } - if (shaderA == mShaderA && shaderB == mShaderB && mPorterDuffMode == nativeMode) { - // no work to do... - return; - } - - discardNativeInstance(); mShaderA = shaderA; mShaderB = shaderB; mPorterDuffMode = nativeMode; @@ -109,16 +76,6 @@ public class ComposeShader extends Shader { mShaderA.getNativeInstance(), mShaderB.getNativeInstance(), mPorterDuffMode); } - @Override - void verifyNativeInstance() { - if (mShaderA.getNativeInstance() != mNativeInstanceShaderA - || mShaderB.getNativeInstance() != mNativeInstanceShaderB) { - // Child shader native instance has been updated, - // so our cached native instance is no longer valid - discard it - discardNativeInstance(); - } - } - /** * @hide */ diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java index b0c145beb649..1578ffb873f0 100644 --- a/graphics/java/android/graphics/LightingColorFilter.java +++ b/graphics/java/android/graphics/LightingColorFilter.java @@ -57,8 +57,6 @@ public class LightingColorFilter extends ColorFilter { /** * Returns the RGB color used to multiply the source color when the * color filter is applied. - * - * @see #setColorMultiply(int) */ @ColorInt public int getColorMultiply() { @@ -71,6 +69,8 @@ public class LightingColorFilter extends ColorFilter { * The alpha channel of this color is ignored. * * @see #getColorMultiply() + * + * @hide */ public void setColorMultiply(@ColorInt int mul) { if (mMul != mul) { @@ -82,8 +82,6 @@ public class LightingColorFilter extends ColorFilter { /** * Returns the RGB color that will be added to the source color * when the color filter is applied. - * - * @see #setColorAdd(int) */ @ColorInt public int getColorAdd() { @@ -96,6 +94,8 @@ public class LightingColorFilter extends ColorFilter { * The alpha channel of this color is ignored. * * @see #getColorAdd() + * + * @hide */ public void setColorAdd(@ColorInt int add) { if (mAdd != add) { diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java index 0e4cd0ae06b6..7139efec9337 100644 --- a/graphics/java/android/graphics/LinearGradient.java +++ b/graphics/java/android/graphics/LinearGradient.java @@ -57,48 +57,12 @@ public class LinearGradient extends Shader { */ public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) { - set(x0, y0, x1, y1, colors, positions, tile); - } - - /** - * Create a shader that draws a linear gradient along a line. - * - * @param x0 The x-coordinate for the start of the gradient line - * @param y0 The y-coordinate for the start of the gradient line - * @param x1 The x-coordinate for the end of the gradient line - * @param y1 The y-coordinate for the end of the gradient line - * @param color0 The color at the start of the gradient line. - * @param color1 The color at the end of the gradient line. - * @param tile The Shader tiling mode - */ - public LinearGradient(float x0, float y0, float x1, float y1, - @ColorInt int color0, @ColorInt int color1, - @NonNull TileMode tile) { - set(x0, y0, x1, y1, color0, color1, tile); - } - - /** - * Reinitialize the shader. - * - * @param x0 The x-coordinate for the start of the gradient line - * @param y0 The y-coordinate for the start of the gradient line - * @param x1 The x-coordinate for the end of the gradient line - * @param y1 The y-coordinate for the end of the gradient line - * @param colors The colors to be distributed along the gradient line - * @param positions May be null. The relative positions [0..1] of - * each corresponding color in the colors array. If this is null, - * the the colors are distributed evenly along the gradient line. - * @param tile The Shader tiling mode - */ - public void set(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], - @Nullable float positions[], @NonNull TileMode tile) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } if (positions != null && colors.length != positions.length) { throw new IllegalArgumentException("color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mX0 = x0; mY0 = y0; @@ -110,7 +74,7 @@ public class LinearGradient extends Shader { } /** - * Reinitialize the shader. + * Create a shader that draws a linear gradient along a line. * * @param x0 The x-coordinate for the start of the gradient line * @param y0 The y-coordinate for the start of the gradient line @@ -120,10 +84,9 @@ public class LinearGradient extends Shader { * @param color1 The color at the end of the gradient line. * @param tile The Shader tiling mode */ - public void set(float x0, float y0, float x1, float y1, + public LinearGradient(float x0, float y0, float x1, float y1, @ColorInt int color0, @ColorInt int color1, @NonNull TileMode tile) { - discardNativeInstance(); mType = TYPE_COLOR_START_AND_COLOR_END; mX0 = x0; mY0 = y0; diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java index ccc6eadc3df5..01d5825dd1e0 100644 --- a/graphics/java/android/graphics/PorterDuffColorFilter.java +++ b/graphics/java/android/graphics/PorterDuffColorFilter.java @@ -49,6 +49,8 @@ public class PorterDuffColorFilter extends ColorFilter { * * @see Color * @see #setColor(int) + * + * @hide */ @ColorInt public int getColor() { @@ -64,6 +66,8 @@ public class PorterDuffColorFilter extends ColorFilter { * @see Color * @see #getColor() * @see #getMode() + * + * @hide */ public void setColor(@ColorInt int color) { if (mColor != color) { @@ -78,6 +82,8 @@ public class PorterDuffColorFilter extends ColorFilter { * * @see PorterDuff * @see #setMode(android.graphics.PorterDuff.Mode) + * + * @hide */ public PorterDuff.Mode getMode() { return mMode; @@ -90,6 +96,8 @@ public class PorterDuffColorFilter extends ColorFilter { * @see PorterDuff * @see #getMode() * @see #getColor() + * + * @hide */ public void setMode(@NonNull PorterDuff.Mode mode) { if (mode == null) { diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java index ae8f7daf0298..f4b11917a415 100644 --- a/graphics/java/android/graphics/RadialGradient.java +++ b/graphics/java/android/graphics/RadialGradient.java @@ -57,39 +57,6 @@ public class RadialGradient extends Shader { public RadialGradient(float centerX, float centerY, float radius, @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) { - set(centerX, centerY, radius, colors, stops, tileMode); - } - - /** - * Create a shader that draws a radial gradient given the center and radius. - * - * @param centerX The x-coordinate of the center of the radius - * @param centerY The y-coordinate of the center of the radius - * @param radius Must be positive. The radius of the circle for this gradient - * @param centerColor The color at the center of the circle. - * @param edgeColor The color at the edge of the circle. - * @param tileMode The Shader tiling mode - */ - public RadialGradient(float centerX, float centerY, float radius, - @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) { - set(centerX, centerY, radius, centerColor, edgeColor, tileMode); - } - - /** - * Reinitialize the shader. - * - * @param centerX The x-coordinate of the center of the radius - * @param centerY The y-coordinate of the center of the radius - * @param radius Must be positive. The radius of the circle for this gradient. - * @param colors The colors to be distributed between the center and edge of the circle - * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and - * <code>1.0f</code>. The relative position of each corresponding color in - * the colors array. If <code>null</code>, colors are distributed evenly - * between the center and edge of the circle. - * @param tileMode The Shader tiling mode - */ - public void set(float centerX, float centerY, float radius, - @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) { if (radius <= 0) { throw new IllegalArgumentException("radius must be > 0"); } @@ -99,7 +66,6 @@ public class RadialGradient extends Shader { if (stops != null && colors.length != stops.length) { throw new IllegalArgumentException("color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mX = centerX; mY = centerY; @@ -110,7 +76,7 @@ public class RadialGradient extends Shader { } /** - * Reinitialize the shader. + * Create a shader that draws a radial gradient given the center and radius. * * @param centerX The x-coordinate of the center of the radius * @param centerY The y-coordinate of the center of the radius @@ -119,12 +85,11 @@ public class RadialGradient extends Shader { * @param edgeColor The color at the edge of the circle. * @param tileMode The Shader tiling mode */ - public void set(float centerX, float centerY, float radius, + public RadialGradient(float centerX, float centerY, float radius, @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) { if (radius <= 0) { throw new IllegalArgumentException("radius must be > 0"); } - discardNativeInstance(); mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE; mX = centerX; mY = centerY; diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java index 8410ab2a1e02..c7447575bd8f 100644 --- a/graphics/java/android/graphics/Shader.java +++ b/graphics/java/android/graphics/Shader.java @@ -105,20 +105,13 @@ public class Shader { return 0; } - void discardNativeInstance() { + private void discardNativeInstance() { if (mNativeInstance != 0) { nativeSafeUnref(mNativeInstance); mNativeInstance = 0; } } - /** - * Callback for subclasses to call {@link #discardNativeInstance()} if the most recently - * constructed native instance is no longer valid. - */ - void verifyNativeInstance() { - } - @Override protected void finalize() throws Throwable { try { @@ -155,9 +148,6 @@ public class Shader { throw new IllegalStateException("attempting to use a finalized Shader"); } - // verify mNativeInstance is valid - verifyNativeInstance(); - if (mNativeInstance == 0) { mNativeInstance = createNativeInstance(mLocalMatrix == null ? 0 : mLocalMatrix.native_instance); diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java index 0a1aef605562..b6b80b4f57dc 100644 --- a/graphics/java/android/graphics/SweepGradient.java +++ b/graphics/java/android/graphics/SweepGradient.java @@ -54,37 +54,6 @@ public class SweepGradient extends Shader { */ public SweepGradient(float cx, float cy, @NonNull @ColorInt int colors[], @Nullable float positions[]) { - set(cx, cy, colors, positions); - } - - /** - * A Shader that draws a sweep gradient around a center point. - * - * @param cx The x-coordinate of the center - * @param cy The y-coordinate of the center - * @param color0 The color to use at the start of the sweep - * @param color1 The color to use at the end of the sweep - */ - public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { - set(cx, cy, color0, color1); - } - - /** - * Reinitialize the shader. - * - * @param cx The x-coordinate of the center - * @param cy The y-coordinate of the center - * @param colors The colors to be distributed between around the center. - * There must be at least 2 colors in the array. - * @param positions May be NULL. The relative position of - * each corresponding color in the colors array, beginning - * with 0 and ending with 1.0. If the values are not - * monotonic, the drawing may produce unexpected results. - * If positions is NULL, then the colors are automatically - * spaced evenly. - */ - public void set(float cx, float cy, - @NonNull @ColorInt int colors[], @Nullable float positions[]) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } @@ -92,7 +61,6 @@ public class SweepGradient extends Shader { throw new IllegalArgumentException( "color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mCx = cx; mCy = cy; @@ -101,15 +69,14 @@ public class SweepGradient extends Shader { } /** - * Reinitialize the shader. + * A Shader that draws a sweep gradient around a center point. * * @param cx The x-coordinate of the center * @param cy The y-coordinate of the center * @param color0 The color to use at the start of the sweep * @param color1 The color to use at the end of the sweep */ - public void set(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { - discardNativeInstance(); + public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { mType = TYPE_COLOR_START_AND_COLOR_END; mCx = cx; mCy = cy; diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 9386246bd128..d5efc971d1fd 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1007,13 +1007,14 @@ public class MediaPlayer extends PlayerBase * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @param headers the headers to be sent together with the request for the data - * Note that the cross domain redirection is allowed by default, but that can be - * changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value - * to disallow or allow cross domain redirection. * The headers must not include cookies. Instead, use the cookies param. * @param cookies the cookies to be sent together with the request * @throws IllegalStateException if it is called in an invalid state + * + * <p><strong>Note</strong> that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. */ public void setDataSource(@NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) @@ -1056,11 +1057,12 @@ public class MediaPlayer extends PlayerBase * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @param headers the headers to be sent together with the request for the data - * Note that the cross domain redirection is allowed by default, but that can be - * changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value - * to disallow or allow cross domain redirection. * @throws IllegalStateException if it is called in an invalid state + * + * <p><strong>Note</strong> that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. */ public void setDataSource(@NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers) @@ -1981,7 +1983,7 @@ public class MediaPlayer extends PlayerBase mOnSubtitleDataListener = null; // Modular DRM clean up - mOnDrmConfigListener = null; + mOnDrmConfigHelper = null; mOnDrmInfoHandlerDelegate = null; mOnDrmPreparedHandlerDelegate = null; resetDrmState(); @@ -3905,11 +3907,11 @@ public class MediaPlayer extends PlayerBase * 'securityLevel', which has to be set after DRM scheme creation but * before the DRM session is opened. * - * The only allowed DRM calls in this listener are getDrmPropertyString - * and setDrmPropertyString. + * The only allowed DRM calls in this listener are {@code getDrmPropertyString} + * and {@code setDrmPropertyString}. * */ - public interface OnDrmConfigListener + public interface OnDrmConfigHelper { /** * Called to give the app the opportunity to configure DRM before the session is created @@ -3922,19 +3924,19 @@ public class MediaPlayer extends PlayerBase /** * Register a callback to be invoked for configuration of the DRM object before * the session is created. - * The callback will be invoked synchronously half-way into the execution + * The callback will be invoked synchronously during the execution * of {@link #prepareDrm(UUID uuid)}. * * @param listener the callback that will be run */ - public void setOnDrmConfigListener(OnDrmConfigListener listener) + public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { synchronized (mDrmLock) { - mOnDrmConfigListener = listener; + mOnDrmConfigHelper = listener; } // synchronized } - private OnDrmConfigListener mOnDrmConfigListener; + private OnDrmConfigHelper mOnDrmConfigHelper; /** * Interface definition of a callback to be invoked when the @@ -3946,7 +3948,7 @@ public class MediaPlayer extends PlayerBase * Called to indicate DRM info is available * * @param mp the {@code MediaPlayer} associated with this callback - * @param drmInfo DRM info of the source including PSSH, mimes, and subset + * @param drmInfo DRM info of the source including PSSH, and subset * of crypto schemes supported by this device */ public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo); @@ -3982,6 +3984,41 @@ public class MediaPlayer extends PlayerBase private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate; + + /** + * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener. + * <p> + * + * DRM preparation has succeeded. + */ + public static final int PREPARE_DRM_STATUS_SUCCESS = 0; + + /** + * The device required DRM provisioning but couldn't reach the provisioning server. + */ + public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; + + /** + * The device required DRM provisioning but the provisioning server denied the request. + */ + public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; + + /** + * The DRM preparation has failed . + */ + public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; + + + /** @hide */ + @IntDef({ + PREPARE_DRM_STATUS_SUCCESS, + PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR, + PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR, + PREPARE_DRM_STATUS_PREPARATION_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrepareDrmStatusCode {} + /** * Interface definition of a callback to notify the app when the * DRM is ready for key request/response @@ -3992,9 +4029,13 @@ public class MediaPlayer extends PlayerBase * Called to notify the app that prepareDrm is finished and ready for key request/response * * @param mp the {@code MediaPlayer} associated with this callback - * @param success the result of DRM preparation + * @param status the result of DRM preparation which can be + * {@link #PREPARE_DRM_STATUS_SUCCESS}, + * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR}, + * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or + * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}. */ - public void onDrmPrepared(MediaPlayer mp, boolean success); + public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status); } /** @@ -4038,30 +4079,28 @@ public class MediaPlayer extends PlayerBase mOnDrmInfoListener = listener; // find the looper for our new event handler - Looper looper = null; if (handler != null) { - looper = handler.getLooper(); - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - public void handleMessage(Message msg) { - DrmInfo drmInfo = (DrmInfo)msg.obj; - mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); - } - }; + mHandler = handler; + } else { + // handler == null + // Will let OnDrmInfoListener be called in mEventHandler similar to other + // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be + // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by + // mediaserver). As a result, the callback has to be called directly by + // EventHandler.handleMessage similar to onPrepared. } } void notifyClient(DrmInfo drmInfo) { - if ( mHandler != null ) { - Message msg = new Message(); // no message type needed - msg.obj = drmInfo; - mHandler.sendMessage(msg); + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); + } + }); } - else { // no handler: direct call + else { // no handler: direct call by mEventHandler mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); } } @@ -4078,31 +4117,26 @@ public class MediaPlayer extends PlayerBase mOnDrmPreparedListener = listener; // find the looper for our new event handler - Looper looper = null; if (handler != null) { - looper = handler.getLooper(); - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - public void handleMessage(Message msg) { - boolean success = (msg.arg1 == 0) ? false : true; - mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success); - } - }; + mHandler = handler; + } else if (mEventHandler != null) { + // Otherwise, use mEventHandler + mHandler = mEventHandler; + } else { + Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler"); } } - void notifyClient(boolean success) { - if ( mHandler != null ) { - Message msg = new Message(); // no message type needed - msg.arg1 = success ? 1 : 0; - mHandler.sendMessage(msg); - } - else { // no handler: direct call - mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success); + void notifyClient(int status) { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status); + } + }); + } else { + Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler"); } } } @@ -4137,7 +4171,7 @@ public class MediaPlayer extends PlayerBase /** * Prepares the DRM for the current source * <p> - * If {@code OnDrmConfigListener} is registered, it will be called half-way into + * If {@code OnDrmConfigHelper} is registered, it will be called during * preparation to allow configuration of the DRM properties before opening the * DRM session. Note that the callback is called synchronously in the thread that called * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString} @@ -4148,9 +4182,9 @@ public class MediaPlayer extends PlayerBase * complete depending on the network connectivity. * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking * mode by launching the provisioning in the background and returning. The listener - * will be called when provisioning and preperation has finished. If a + * will be called when provisioning and preparation has finished. If a * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning - * and preperation has finished, i.e., runs in blocking mode. + * and preparation has finished, i.e., runs in blocking mode. * <p> * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM * session being ready. The application should not make any assumption about its call @@ -4158,18 +4192,23 @@ public class MediaPlayer extends PlayerBase * execute the listener (unless the listener is registered with a handler thread). * <p> * - * @param uuid The UUID of the crypto scheme. + * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved + * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}. * - * @throws IllegalStateException if called before prepare(), or there exists a Drm already - * @throws UnsupportedSchemeException if the crypto scheme is not supported - * @throws ResourceBusyException if required DRM resources are in use - * @throws ProvisioningErrorException if provisioning is required but an attempt failed + * @throws IllegalStateException if called before prepare(), or the DRM was + * prepared already + * @throws UnsupportedSchemeException if the crypto scheme is not supported + * @throws ResourceBusyException if required DRM resources are in use + * @throws ProvisioningNetworkErrorException if provisioning is required but failed due to a + * network error + * @throws ProvisioningServerErrorException if provisioning is required but failed due to + * the request denied by the provisioning server */ public void prepareDrm(@NonNull UUID uuid) - throws UnsupportedSchemeException, - ResourceBusyException, ProvisioningErrorException + throws UnsupportedSchemeException, ResourceBusyException, + ProvisioningNetworkErrorException, ProvisioningServerErrorException { - Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigListener: " + mOnDrmConfigListener); + Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper); boolean allDoneWithoutProvisioning = false; // get a snapshot as we'll use them outside the lock @@ -4177,7 +4216,7 @@ public class MediaPlayer extends PlayerBase synchronized (mDrmLock) { - // only allowing if tied to a protected source; might releax for releasing offline keys + // only allowing if tied to a protected source; might relax for releasing offline keys if (mDrmInfo == null) { final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " + "DRM info be retrieved before this call."; @@ -4226,8 +4265,8 @@ public class MediaPlayer extends PlayerBase // call the callback outside the lock - if (mOnDrmConfigListener != null) { - mOnDrmConfigListener.onDrmConfig(this); + if (mOnDrmConfigHelper != null) { + mOnDrmConfigHelper.onDrmConfig(this); } synchronized (mDrmLock) { @@ -4251,15 +4290,33 @@ public class MediaPlayer extends PlayerBase Log.w(TAG, "prepareDrm: NotProvisionedException"); // handle provisioning internally; it'll reset mPrepareDrmInProgress - boolean result = HandleProvisioninig(uuid); + int result = HandleProvisioninig(uuid); // if blocking mode, we're already done; // if non-blocking mode, we attempted to launch background provisioning - if (result == false) { - final String msg = "prepareDrm: Provisioning was required but failed."; - Log.e(TAG, msg); + if (result != PREPARE_DRM_STATUS_SUCCESS) { earlyExit = true; - throw new ProvisioningErrorException(msg); + String msg; + + switch (result) { + case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR: + msg = "prepareDrm: Provisioning was required but failed " + + "due to a network error."; + Log.e(TAG, msg); + throw new ProvisioningNetworkErrorException(msg); + + case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR: + msg = "prepareDrm: Provisioning was required but the request " + + "was denied by the server."; + Log.e(TAG, msg); + throw new ProvisioningServerErrorException(msg); + + case PREPARE_DRM_STATUS_PREPARATION_ERROR: + default: // default for safeguard + msg = "prepareDrm: Post-provisioning preparation failed."; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } } // nothing else to do; // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup @@ -4281,7 +4338,7 @@ public class MediaPlayer extends PlayerBase // if finished successfully without provisioning, call the callback outside the lock if (allDoneWithoutProvisioning) { if (onDrmPreparedHandlerDelegate != null) - onDrmPreparedHandlerDelegate.notifyClient(true /*success*/); + onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS); } } @@ -4291,6 +4348,10 @@ public class MediaPlayer extends PlayerBase /** * Releases the DRM session + * <p> + * The player has to have an active DRM session and be in stopped, or prepared + * state before this call is made. + * A {@code reset()} call will release the DRM session implicitly. * * @throws NoDrmSchemeException if there is no active DRM session to release */ @@ -4307,7 +4368,7 @@ public class MediaPlayer extends PlayerBase try { // we don't have the player's state in this layer. The below call raises - // exception if we're in a non-stopped/idle state. + // exception if we're in a non-stopped/prepared state. // for cleaning native/mediaserver crypto object _releaseDrm(); @@ -4316,9 +4377,11 @@ public class MediaPlayer extends PlayerBase cleanDrmObj(); mActiveDrmScheme = false; - } catch (Exception e) { + } catch (IllegalStateException e) { Log.w(TAG, "releaseDrm: Exception ", e); - throw e; + throw new IllegalStateException("releaseDrm: The player is not in a valid state."); + } catch (Exception e) { + Log.e(TAG, "releaseDrm: Exception ", e); } } // synchronized } @@ -4337,21 +4400,23 @@ public class MediaPlayer extends PlayerBase * it should deliver to the response to the DRM engine plugin using the method * {@link #provideKeyResponse}. * - * @param scope may be a container-specific initialization data or a keySetId, - * depending on the specified keyType. - * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set to - * the container-specific initialization data. Its meaning is interpreted based on the - * mime type provided in the mimeType parameter. It could contain, for example, - * the content ID, key ID or other data obtained from the content metadata that is - * required in generating the key request. - * When the keyType is KEY_TYPE_RELEASE, scope should be set to the keySetId of - * the keys being released. + * @param keySetId is the key-set identifier of the offline keys being released when keyType is + * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when + * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. + * + * @param initData is the container-specific initialization data when the keyType is + * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is + * interpreted based on the mime type provided in the mimeType parameter. It could + * contain, for example, the content ID, key ID or other data obtained from the content + * metadata that is required in generating the key request. + * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null. * * @param mimeType identifies the mime type of the content * - * @param keyType specifes the type of the request. The request may be to acquire - * keys for streaming or offline content, or to release previously acquired - * keys, which are identified by a keySetId. + * @param keyType specifies the type of the request. The request may be to acquire + * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content + * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired + * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId. * * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. @@ -4360,12 +4425,13 @@ public class MediaPlayer extends PlayerBase * @throws NoDrmSchemeException if there is no active DRM session */ @NonNull - public MediaDrm.KeyRequest getKeyRequest(@NonNull byte[] scope, @Nullable String mimeType, - @MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters) + public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData, + @Nullable String mimeType, @MediaDrm.KeyType int keyType, + @Nullable Map<String, String> optionalParameters) throws NoDrmSchemeException { Log.v(TAG, "getKeyRequest: " + - " scope: " + scope + " mimeType: " + mimeType + + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType + " keyType: " + keyType + " optionalParameters: " + optionalParameters); synchronized (mDrmLock) { @@ -4375,20 +4441,16 @@ public class MediaPlayer extends PlayerBase } try { - byte[] scopeOut = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? - mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE - scope; // keySetId for KEY_TYPE_RELEASE - - byte[] initData = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? - scope : // initData for KEY_TYPE_STREAMING/OFFLINE - null; // not used for KEY_TYPE_RELEASE + byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? + mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE + keySetId; // keySetId for KEY_TYPE_RELEASE HashMap<String, String> hmapOptionalParameters = (optionalParameters != null) ? new HashMap<String, String>(optionalParameters) : null; - MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scopeOut, initData, mimeType, + MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType, keyType, hmapOptionalParameters); Log.v(TAG, "getKeyRequest: --> request: " + request); @@ -4499,8 +4561,8 @@ public class MediaPlayer extends PlayerBase * @param propertyName the property name * * Standard fields names are: - * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION}, - * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS} + * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, + * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} */ @NonNull public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName) @@ -4537,8 +4599,8 @@ public class MediaPlayer extends PlayerBase * @param value the property value * * Standard fields names are: - * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION}, - * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS} + * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, + * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} */ public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value) @@ -4565,8 +4627,6 @@ public class MediaPlayer extends PlayerBase public static final class DrmInfo { private Map<UUID, byte[]> mapPssh; private UUID[] supportedSchemes; - // TODO: Won't need this in final release. Only keeping it for the existing test app. - private String[] mimes; public Map<UUID, byte[]> getPssh() { return mapPssh; @@ -4574,15 +4634,10 @@ public class MediaPlayer extends PlayerBase public UUID[] getSupportedSchemes() { return supportedSchemes; } - // TODO: Won't need this in final release. Only keeping it for the existing test app. - public String[] getMimes() { - return mimes; - } - private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes, String[] Mimes) { + private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) { mapPssh = Pssh; supportedSchemes = SupportedSchemes; - mimes = Mimes; } private DrmInfo(Parcel parcel) { @@ -4608,18 +4663,12 @@ public class MediaPlayer extends PlayerBase supportedSchemes[i]); } - // TODO: Won't need this in final release. Only keeping it for the test app. - mimes = parcel.readStringArray(); - int mimeCount = mimes.length; - Log.v(TAG, "DrmInfo() mime: " + Arrays.toString(mimes)); - Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize + - " supportedDRMsCount: " + supportedDRMsCount + - " mimeCount: " + mimeCount); + " supportedDRMsCount: " + supportedDRMsCount); } private DrmInfo makeCopy() { - return new DrmInfo(this.mapPssh, this.supportedSchemes, this.mimes); + return new DrmInfo(this.mapPssh, this.supportedSchemes); } private String arrToHex(byte[] bytes) { @@ -4714,11 +4763,22 @@ public class MediaPlayer extends PlayerBase /** * Thrown when the device requires DRM provisioning but the provisioning attempt has - * failed (for example: network timeout, provisioning server error). + * failed due to a network error (Internet reachability, timeout, etc.). * Extends MediaDrm.MediaDrmException */ - public static final class ProvisioningErrorException extends MediaDrmException { - public ProvisioningErrorException(String detailMessage) { + public static final class ProvisioningNetworkErrorException extends MediaDrmException { + public ProvisioningNetworkErrorException(String detailMessage) { + super(detailMessage); + } + } + + /** + * Thrown when the device requires DRM provisioning but the provisioning attempt has + * failed due to the provisioning server denying the request. + * Extends MediaDrm.MediaDrmException + */ + public static final class ProvisioningServerErrorException extends MediaDrmException { + public ProvisioningServerErrorException(String detailMessage) { super(detailMessage); } } @@ -4770,14 +4830,13 @@ public class MediaPlayer extends PlayerBase private UUID uuid; private String urlStr; - private byte[] response; private Object drmLock; private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate; private MediaPlayer mediaPlayer; - private boolean succeeded; + private int status; private boolean finished; - public boolean succeeded() { - return succeeded; + public int status() { + return status; } public ProvisioningThread initialize(MediaDrm.ProvisionRequest request, @@ -4790,12 +4849,15 @@ public class MediaPlayer extends PlayerBase urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); this.uuid = uuid; + status = PREPARE_DRM_STATUS_PREPARATION_ERROR; + Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr); return this; } public void run() { + byte[] response = null; boolean provisioningSucceeded = false; try { URL url = new URL(urlStr); @@ -4813,11 +4875,13 @@ public class MediaPlayer extends PlayerBase Log.v(TAG, "HandleProvisioninig: Thread run: response " + response.length + " " + response); } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url); } finally { connection.disconnect(); } } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e); } @@ -4828,12 +4892,15 @@ public class MediaPlayer extends PlayerBase "provideProvisionResponse SUCCEEDED!"); provisioningSucceeded = true; - } catch (Exception e) { + } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: " + "provideProvisionResponse " + e); } } + boolean succeeded = false; + // non-blocking mode needs the lock if (onDrmPreparedHandlerDelegate != null) { @@ -4841,6 +4908,9 @@ public class MediaPlayer extends PlayerBase // continuing with prepareDrm if (provisioningSucceeded) { succeeded = mediaPlayer.resumePrepareDrm(uuid); + status = (succeeded) ? + PREPARE_DRM_STATUS_SUCCESS : + PREPARE_DRM_STATUS_PREPARATION_ERROR; } mediaPlayer.mDrmProvisioningInProgress = false; mediaPlayer.mPrepareDrmInProgress = false; @@ -4850,12 +4920,15 @@ public class MediaPlayer extends PlayerBase } // synchronized // calling the callback outside the lock - onDrmPreparedHandlerDelegate.notifyClient(succeeded); + onDrmPreparedHandlerDelegate.notifyClient(status); } else { // blocking mode already has the lock // continuing with prepareDrm if (provisioningSucceeded) { succeeded = mediaPlayer.resumePrepareDrm(uuid); + status = (succeeded) ? + PREPARE_DRM_STATUS_SUCCESS : + PREPARE_DRM_STATUS_PREPARATION_ERROR; } mediaPlayer.mDrmProvisioningInProgress = false; mediaPlayer.mPrepareDrmInProgress = false; @@ -4869,19 +4942,19 @@ public class MediaPlayer extends PlayerBase } // ProvisioningThread - private boolean HandleProvisioninig(UUID uuid) + private int HandleProvisioninig(UUID uuid) { // the lock is already held by the caller if (mDrmProvisioningInProgress) { Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress"); - return false; + return PREPARE_DRM_STATUS_PREPARATION_ERROR; } MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); if (provReq == null) { Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null."); - return false; + return PREPARE_DRM_STATUS_PREPARATION_ERROR; } Log.v(TAG, "HandleProvisioninig provReq " + @@ -4893,11 +4966,11 @@ public class MediaPlayer extends PlayerBase mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this); mDrmProvisioningThread.start(); - boolean result = false; + int result; - // non-blocking + // non-blocking: this is not the final result if (mOnDrmPreparedHandlerDelegate != null) { - result = true; + result = PREPARE_DRM_STATUS_SUCCESS; } else { // if blocking mode, wait till provisioning is done try { @@ -4905,7 +4978,7 @@ public class MediaPlayer extends PlayerBase } catch (Exception e) { Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e); } - result = mDrmProvisioningThread.succeeded(); + result = mDrmProvisioningThread.status(); // no longer need the thread mDrmProvisioningThread = null; } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index 246bd2bc7ce0..5beb41253d48 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -314,8 +314,8 @@ public class DeviceDiscoveryService extends Service { } class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { - //TODO wifi icon private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); + private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); private Drawable icon(int drawableRes) { Drawable icon = getResources().getDrawable(drawableRes, null); @@ -345,6 +345,11 @@ public class DeviceDiscoveryService extends Service { device.equals(mSelectedDevice) ? Color.GRAY : Color.TRANSPARENT); + textView.setCompoundDrawablesWithIntrinsicBounds( + device.device instanceof android.net.wifi.ScanResult + ? WIFI_ICON + : BLUETOOTH_ICON, + null, null, null); textView.setOnClickListener((view) -> { mSelectedDevice = device; notifyDataSetChanged(); @@ -357,8 +362,6 @@ public class DeviceDiscoveryService extends Service { textView.setTextColor(Color.BLACK); final int padding = DeviceChooserActivity.getPadding(getResources()); textView.setPadding(padding, padding, padding, padding); - textView.setCompoundDrawablesWithIntrinsicBounds( - BLUETOOTH_ICON, null, null, null); textView.setCompoundDrawablePadding(padding); return textView; } diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 33b1dd40e471..734b9ee76435 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -376,13 +376,13 @@ <string name="accessibility_no_sim">No SIM.</string> <!-- Content description of the cell data. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data">Cellular Data</string> + <string name="accessibility_cell_data">Mobile Data</string> <!-- Content description of the cell data being enabled. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data_on">Cellular Data On</string> + <string name="accessibility_cell_data_on">Mobile Data On</string> <!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data_off">Cellular Data Off</string> + <string name="accessibility_cell_data_off">Mobile Data Off</string> <!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_bluetooth_tether">Bluetooth tethering.</string> @@ -574,11 +574,11 @@ <!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> <string name="data_usage_disabled_dialog_4g_title">4G data is paused</string> <!-- Title of dialog shown when mobile data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> - <string name="data_usage_disabled_dialog_mobile_title">Cellular data is paused</string> + <string name="data_usage_disabled_dialog_mobile_title">Mobile data is paused</string> <!-- Title of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> <string name="data_usage_disabled_dialog_title">Data is paused</string> <!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] --> - <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using cellular data.\n\nIf you resume, charges may apply for data usage.</string> + <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage.</string> <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] --> <string name="data_usage_disabled_dialog_enable">Resume</string> @@ -749,7 +749,7 @@ <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] --> <string name="quick_settings_flashlight_label">Flashlight</string> <!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] --> - <string name="quick_settings_cellular_detail_title">Cellular data</string> + <string name="quick_settings_cellular_detail_title">Mobile data</string> <!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] --> <string name="quick_settings_cellular_detail_data_usage">Data usage</string> <!-- QuickSettings: Cellular detail panel, remaining data title [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java index 86bb0deff067..4b3cdfbcd71c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java @@ -39,7 +39,8 @@ public class PipDismissViewController { // This delay controls how long to wait before we show the target when the user first moves // the PIP, to prevent the target from animating if the user just wants to fling the PIP private static final int SHOW_TARGET_DELAY = 100; - private static final int SHOW_TARGET_DURATION = 200; + private static final int SHOW_TARGET_DURATION = 350; + private static final int HIDE_TARGET_DURATION = 225; private Context mContext; private WindowManager mWindowManager; @@ -96,7 +97,7 @@ public class PipDismissViewController { public void showDismissTarget() { mDismissView.animate() .alpha(1f) - .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .setInterpolator(Interpolators.LINEAR) .setStartDelay(SHOW_TARGET_DELAY) .setDuration(SHOW_TARGET_DURATION) .start(); @@ -109,9 +110,9 @@ public class PipDismissViewController { if (mDismissView != null) { mDismissView.animate() .alpha(0f) - .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) + .setInterpolator(Interpolators.LINEAR) .setStartDelay(0) - .setDuration(SHOW_TARGET_DURATION) + .setDuration(HIDE_TARGET_DURATION) .withEndAction(new Runnable() { @Override public void run() { 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 fbf7ff21b0ce..3223f9fd52b1 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -63,7 +63,7 @@ public class PipTouchHandler implements TunerService.Tunable { private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0; private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1; - private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200; + private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225; // Allow dragging the PIP to a location to close it private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true; diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index d6f525655d56..ad7ff1b5b5a8 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -184,7 +184,7 @@ public final class AutofillManagerService extends SystemService { } @Override - public void onStopUser(int userId) { + public void onCleanupUser(int userId) { synchronized (mLock) { removeCachedServiceLocked(userId); } @@ -211,7 +211,7 @@ public final class AutofillManagerService extends SystemService { /** * Peeks the service instance for a user. * - * @return service instance or null if not already present + * @return service instance or {@code null} if not already present */ @Nullable AutofillManagerServiceImpl peekServiceForUserLocked(int userId) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 7c3f3245461c..c92c52fde8eb 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -442,7 +442,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ public boolean showSaveLocked() { if (mStructure == null) { - Slog.wtf(TAG, "showSaveLocked(): no mStructure"); + Slog.d(TAG, "showSaveLocked(): no mStructure"); return true; } if (mResponses == null) { @@ -764,7 +764,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState boolean saveOnAllViewsInvisible = false; SaveInfo saveInfo = mResponses.valueAt(getLastResponseIndex()).getSaveInfo(); if (saveInfo != null) { - saveOnAllViewsInvisible = saveInfo.saveOnAllViewsInvisible(); + saveOnAllViewsInvisible = + (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0; // We only need to track views if we want to save once they become invisible. if (saveOnAllViewsInvisible) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 17c7dde11726..7cdddc0ffeaf 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1178,6 +1178,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * the activity is not currently visible and {@param noThrow} is not set. */ boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) { + if (!supportsPictureInPicture()) { + return false; + } + // Check app-ops and see if PiP is supported for this package if (!checkEnterPictureInPictureAppOpsState()) { return false; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 825e8ac0a698..85c5c647ceed 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -65,6 +65,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; +import static com.android.server.am.ActivityStack.ActivityState.STOPPED; +import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStackSupervisor.FindTaskResult; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY; @@ -1179,7 +1181,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED + if (r.state == STOPPING || r.state == STOPPED || r.state == ActivityState.PAUSED || r.state == ActivityState.PAUSING) { r.setSleeping(true); } @@ -1362,7 +1364,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev); if (prev != null) { - final boolean wasStopping = prev.state == ActivityState.STOPPING; + final boolean wasStopping = prev.state == STOPPING; prev.state = ActivityState.PAUSED; if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev); @@ -1383,7 +1385,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. - prev.state = ActivityState.STOPPING; + prev.state = STOPPING; } else if ((!prev.visible && !hasVisibleBehindActivity()) || mService.isSleepingOrShuttingDownLocked()) { // If we were visible then resumeTopActivities will release resources before @@ -2002,10 +2004,17 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // keeping the screen frozen. if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state); try { + final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( + "makeInvisible", true /* noThrow */, true /* beforeStopping */); + // We don't want to call setVisible(false) to avoid notifying the client of this + // intermittent invisible state if it can enter Pip and isn't stopped or stopping. + if (!canEnterPictureInPicture || r.state == STOPPING || r.state == STOPPED) { + r.setVisible(false); + } + switch (r.state) { case STOPPING: case STOPPED: - r.setVisible(false); if (r.app != null && r.app.thread != null) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + r); @@ -2024,25 +2033,16 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // This case created for transitioning activities from // translucent to opaque {@link Activity#convertToOpaque}. if (visibleBehind == r) { - r.setVisible(false); releaseBackgroundResources(r); } else { // If this activity is in a state where it can currently enter // picture-in-picture, then don't immediately schedule the idle now in case // the activity tries to enterPictureInPictureMode() later. Otherwise, // we will try and stop the activity next time idle is processed. - final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( - "makeInvisible", true /* noThrow */, true /* beforeStopping */); if (canEnterPictureInPicture) { - // We set r.visible=false so that Stop will later - // call setVisible for us. In this case - // we don't want to call setVisible(false) to avoid - // notifying the client of this intermittent invisible - // state. + // We set r.visible=false so that Stop will later call setVisible for us r.visible = false; - } else { - r.setVisible(false); } addToStopping(r, true /* scheduleIdle */, canEnterPictureInPicture /* idleDelayed */); @@ -2318,9 +2318,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid); + final boolean prevCanPip = prev != null && prev.checkEnterPictureInPictureState( + "resumeTopActivity", true /* noThrow */, userLeaving /* beforeStopping */); // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity - // to be paused, while at the same time resuming the new resume activity + // to be paused, while at the same time resuming the new resume activity only if the + // previous activity can't go into Pip since we want to give Pip activities a chance to + // enter Pip before resuming the next activity. final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0; + // TODO: This would be go to have however, the various call points that pass in + // prev need to be corrected first. In some cases the prev is equal to the next e.g. launch + // an app from home. And, is come other cases it is null e.g. press home button after + // launching an app. The doc on the method says prev. is null expect for the case we are + // coming from pause. We need to see if that is a valid thing and also if all the code in + // this method using prev. are setup to function like that. + //&& !prevCanPip; boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false); if (mResumedActivity != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, @@ -3360,11 +3371,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.stopped = false; if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: " + r + " (stop requested)"); - r.state = ActivityState.STOPPING; + r.state = STOPPING; if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Stopping visible=" + r.visible + " for " + r); if (!r.visible) { - r.setVisibility(false); + r.setVisible(false); } EventLogTags.writeAmStopActivity( r.userId, System.identityHashCode(r), r.shortComponentName); @@ -3382,7 +3393,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Just in case, assume it to be stopped. r.stopped = true; if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r); - r.state = ActivityState.STOPPED; + r.state = STOPPED; if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-except"); } @@ -3687,7 +3698,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); - r.state = ActivityState.STOPPING; + r.state = STOPPING; if (oomAdj) { mService.updateOomAdjLocked(); } @@ -3712,8 +3723,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || (prevState == ActivityState.PAUSED && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID)) || finishingActivityInNonFocusedStack - || prevState == ActivityState.STOPPING - || prevState == ActivityState.STOPPED + || prevState == STOPPING + || prevState == STOPPED || prevState == ActivityState.INITIALIZING) { r.makeFinishingLocked(); boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm"); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 43ae4b22ba63..152d5f463988 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2878,6 +2878,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mWindowManager.deferSurfaceLayout(); + // This will clear the pinned stack by moving an existing task to the full screen stack, + // ensuring only one task is present. + moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP); + // Need to make sure the pinned stack exist so we can resize it below... final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index f8a4d4b21414..d42b6a7647ce 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -48,6 +48,7 @@ import android.service.voice.IVoiceInteractionSession; import android.util.DisplayMetrics; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.util.XmlUtils; @@ -445,10 +446,23 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta final Rect bounds = updateOverrideConfigurationFromLaunchBounds(); final Configuration overrideConfig = getOverrideConfiguration(); - mWindowContainerController = new TaskWindowContainerController(taskId, this, + setWindowContainerController(new TaskWindowContainerController(taskId, this, getStack().getWindowContainerController(), userId, bounds, overrideConfig, mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers, - lastTaskDescription); + lastTaskDescription)); + } + + /** + * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}. + */ + @VisibleForTesting + protected void setWindowContainerController(TaskWindowContainerController controller) { + if (mWindowContainerController != null) { + throw new IllegalArgumentException("Window container=" + mWindowContainerController + + " already created for task=" + this); + } + + mWindowContainerController = controller; } void removeWindowContainer() { diff --git a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java index ce976d29216b..acedafc4025c 100644 --- a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java +++ b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java @@ -15,19 +15,25 @@ */ package com.android.server.notification; +import android.util.Slog; + import java.util.Comparator; /** * Sorts notifications by their global sort key. */ public class GlobalSortKeyComparator implements Comparator<NotificationRecord> { + private final static String TAG = "GlobalSortComp"; + @Override public int compare(NotificationRecord left, NotificationRecord right) { if (left.getGlobalSortKey() == null) { - throw new IllegalStateException("Missing left global sort key: " + left); + Slog.wtf(TAG, "Missing left global sort key: " + left); + return 1; } if (right.getGlobalSortKey() == null) { - throw new IllegalStateException("Missing right global sort key: " + right); + Slog.wtf(TAG, "Missing right global sort key: " + right); + return -1; } return left.getGlobalSortKey().compareTo(right.getGlobalSortKey()); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f6addc0d87e8..8a6a940e6669 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1874,10 +1874,9 @@ public class NotificationManagerService extends SystemService { int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), incomingUserId, true, false, "getAppActiveNotifications", pkg); - final ArrayMap<String, StatusBarNotification> map - = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size()); - synchronized (mNotificationLock) { + final ArrayMap<String, StatusBarNotification> map + = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size()); final int N = mNotificationList.size(); for (int i = 0; i < N; i++) { StatusBarNotification sbn = sanitizeSbn(pkg, userId, @@ -1900,11 +1899,10 @@ public class NotificationManagerService extends SystemService { map.put(sbn.getKey(), sbn); // pending update overwrites existing post here } } + final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size()); + list.addAll(map.values()); + return new ParceledListSlice<StatusBarNotification>(list); } - - final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size()); - list.addAll(map.values()); - return new ParceledListSlice<StatusBarNotification>(list); } private StatusBarNotification sanitizeSbn(String pkg, int userId, @@ -2036,8 +2034,10 @@ public class NotificationManagerService extends SystemService { long identity = Binder.clearCallingIdentity(); try { // allow bound services to disable themselves - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - info.getOwner().setComponentState(info.component, false); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + info.getOwner().setComponentState(info.component, false); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2101,8 +2101,10 @@ public class NotificationManagerService extends SystemService { String key, String snoozeCriterionId) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2118,8 +2120,10 @@ public class NotificationManagerService extends SystemService { long duration) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - snoozeNotificationInt(key, duration, null, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + snoozeNotificationInt(key, duration, null, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2134,9 +2138,11 @@ public class NotificationManagerService extends SystemService { public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = - mNotificationAssistants.checkServiceTokenLocked(token); - unsnoozeNotificationInt(key, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = + mNotificationAssistants.checkServiceTokenLocked(token); + unsnoozeNotificationInt(key, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2734,7 +2740,10 @@ public class NotificationManagerService extends SystemService { } private void verifyPrivilegedListener(INotificationListener token, UserHandle user) { - ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + ManagedServiceInfo info; + synchronized (mNotificationLock) { + info = mListeners.checkServiceTokenLocked(token); + } if (!hasCompanionDevice(info)) { throw new SecurityException(info + " does not have access"); } @@ -3099,8 +3108,10 @@ public class NotificationManagerService extends SystemService { @Override public void run() { synchronized (mNotificationLock) { - removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId); - removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId); + removeForegroundServiceFlagByListLocked( + mEnqueuedNotifications, pkg, notificationId, userId); + removeForegroundServiceFlagByListLocked( + mNotificationList, pkg, notificationId, userId); } } }); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bfa1b9988b2f..e82ba9cee919 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -742,8 +742,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean forceEphemeralUsers = false; // Can only be set by a device owner. boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner. - // one notification after enabling + 3 more after reboots - static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4; + // one notification after enabling + one more after reboots + static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2; int numNetworkLoggingNotifications = 0; long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java new file mode 100644 index 000000000000..24cb72e8b0fb --- /dev/null +++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java @@ -0,0 +1,153 @@ +/* + * 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.server.notification; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class GlobalSortKeyComparatorTest { + + private final String PKG = "PKG"; + private final int UID = 1111111; + private static final String TEST_CHANNEL_ID = "test_channel_id"; + + @Test + public void testComparator() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + left.setGlobalSortKey("first"); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + right.setGlobalSortKey("second"); + + NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(left); + expected.add(right); + expected.add(last); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + @Test + public void testNoCrash_leftNull() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + right.setGlobalSortKey("not null"); + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(right); + expected.add(left); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + @Test + public void testNoCrash_rightNull() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + left.setGlobalSortKey("not null"); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(left); + expected.add(right); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + private NotificationChannel getDefaultChannel() { + return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", + NotificationManager.IMPORTANCE_LOW); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index 54ecab3af542..f75d49cae574 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -16,7 +16,8 @@ package com.android.server.am; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import android.content.ComponentName; import android.platform.test.annotations.Presubmit; @@ -36,50 +37,61 @@ import org.junit.Test; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityRecordTests extends ActivityTestsBase { + private static final int TEST_STACK_ID = 100; + private final ComponentName testActivityComponent = ComponentName.unflattenFromString("com.foo/.BarActivity"); @Test public void testStackCleanupOnClearingTask() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); record.setTask(null); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1); } @Test public void testStackCleanupOnActivityRemoval() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); task.removeActivity(record); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1); } @Test public void testStackCleanupOnTaskRemoval() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); - testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + service.mStackSupervisor.getStack(TEST_STACK_ID) + .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); + + // Stack should be gone on task removal. + assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID)); } @Test public void testNoCleanupMovingActivityInSameStack() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord oldTask = createTask(service, testActivityComponent, testStack); + final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, oldTask); - final TaskRecord newTask = createTask(service, testActivityComponent, testStack); + final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID); record.reparent(newTask, 0, null /*reason*/); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0); + } + + private static int getActivityRemovedFromStackCount(ActivityManagerService service, + int stackId) { + final ActivityStack stack = service.mStackSupervisor.getStack(stackId); + if (stack instanceof ActivityStackReporter) { + return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount(); + } + + return -1; } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java index 8423affecd30..fc9ab9635c57 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -16,8 +16,14 @@ package com.android.server.am; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import android.content.ComponentName; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -25,6 +31,10 @@ import android.support.test.runner.AndroidJUnit4; import org.junit.runner.RunWith; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; /** @@ -37,6 +47,9 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackSupervisorTests extends ActivityTestsBase { + private final ComponentName testActivityComponent = + ComponentName.unflattenFromString("com.foo/.BarActivity"); + /** * This test ensures that we do not try to restore a task based off an invalid task id. The * stack supervisor is a test version so there will be no tasks present. We should expect @@ -49,4 +62,59 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/); assertNull(task); } + + /** + * This test ensures that an existing task in the pinned stack is moved to the fullscreen + * activity stack when a new task is added. + */ + @Test + public void testReplacingTaskInPinnedStack() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TaskRecord firstTask = createTask(service, testActivityComponent, + FULLSCREEN_WORKSPACE_STACK_ID); + final ActivityRecord firstActivity = createActivity(service, testActivityComponent, + firstTask); + // Create a new task on the full screen stack + final TaskRecord secondTask = createTask(service, testActivityComponent, + FULLSCREEN_WORKSPACE_STACK_ID); + final ActivityRecord secondActivity = createActivity(service, testActivityComponent, + secondTask); + service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", + service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID)); + + // Ensure full screen stack has both tasks. + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask, + secondTask); + + // Move first activity to pinned stack. + service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity, + new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove"); + + // Ensure a task has moved over. + ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask); + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask); + + // Move second activity to pinned stack. + service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity, + new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove"); + + // Ensure stacks have swapped tasks. + ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask); + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask); + } + + private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId, + TaskRecord... tasks) { + final ActivityStack stack = supervisor.getStack(stackId); + final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); + assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); + + if (tasks == null) { + return; + } + + for (TaskRecord task : tasks) { + assertTrue(stackTasks.contains(task)); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 1d80b336becc..f42abf1927f0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -37,28 +37,27 @@ import org.junit.Test; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackTests extends ActivityTestsBase { - private final ComponentName testActivityComponent = + private static final int TEST_STACK_ID = 100; + private static final ComponentName testActivityComponent = ComponentName.unflattenFromString("com.foo/.BarActivity"); @Test public void testEmptyTaskCleanupOnRemove() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); assertNotNull(task.getWindowContainerController()); - testStack.removeTask(task, "testEmptyTaskCleanupOnRemove", - ActivityStack.REMOVE_TASK_MODE_DESTROYING); + service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task, + "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING); assertNull(task.getWindowContainerController()); } @Test public void testOccupiedTaskCleanupOnRemove() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task); assertNotNull(task.getWindowContainerController()); - testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove", - ActivityStack.REMOVE_TASK_MODE_DESTROYING); + service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task, + "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING); assertNotNull(task.getWindowContainerController()); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 3bf0e5ff86aa..082708442032 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -17,6 +17,11 @@ package com.android.server.am; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; + +import org.mockito.invocation.InvocationOnMock; import android.app.ActivityManager; import android.content.ComponentName; @@ -33,6 +38,7 @@ import com.android.server.AttributeCache; import com.android.server.wm.AppWindowContainerController; import com.android.server.wm.StackWindowController; +import com.android.server.wm.TaskWindowContainerController; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowTestUtils; import org.junit.After; @@ -64,16 +70,15 @@ public class ActivityTestsBase { protected ActivityManagerService createActivityManagerService() { final ActivityManagerService service = new TestActivityManagerService(mContext); - service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext); + service.mWindowManager = WindowTestUtils.getMockWindowManagerService(); return service; } - protected static TestActivityStack createActivityStack(ActivityManagerService service, + protected static ActivityStack createActivityStack(ActivityManagerService service, int stackId, int displayId, boolean onTop) { if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { - final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor) + return ((TestActivityStackSupervisor) service.mStackSupervisor) .createTestStack(service, stackId, onTop); - return stack; } return null; @@ -103,7 +108,7 @@ public class ActivityTestsBase { } protected static TaskRecord createTask(ActivityManagerService service, - ComponentName component, ActivityStack stack) { + ComponentName component, int stackId) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = component.getPackageName(); @@ -113,13 +118,16 @@ public class ActivityTestsBase { final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo()); + final ActivityStack stack = service.mStackSupervisor.getStack(stackId, + true /*createStaticStackIfNeeded*/, true /*onTop*/); stack.addTask(task, true, "creating test task"); task.setStack(stack); - task.createWindowContainer(true, true); + task.setWindowContainerController(mock(TaskWindowContainerController.class)); return task; } + /** * An {@link ActivityManagerService} subclass which provides a test * {@link ActivityStackSupervisor}. @@ -127,6 +135,9 @@ public class ActivityTestsBase { protected static class TestActivityManagerService extends ActivityManagerService { public TestActivityManagerService(Context context) { super(context); + mSupportsMultiWindow = true; + mSupportsMultiDisplay = true; + mWindowManager = WindowTestUtils.getWindowManagerService(context); } @Override @@ -142,6 +153,12 @@ public class ActivityTestsBase { protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { super(service, looper); + mWindowManager = prepareMockWindowManager(); + } + + // No home stack is set. + @Override + void moveHomeStackToFront(String reason) { } // Invoked during {@link ActivityStack} creation. @@ -149,18 +166,45 @@ public class ActivityTestsBase { void updateUIDsPresentOnDisplay() { } - public TestActivityStack createTestStack(ActivityManagerService service, int stackId, - boolean onTop) { + // Just return the current front task. + @Override + ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) { + return mFocusedStack; + } + + // Called when moving activity to pinned stack. + @Override + void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, + boolean preserveWindows) { + } + + public <T extends ActivityStack> T createTestStack(ActivityManagerService service, + int stackId, boolean onTop) { final ActivityDisplay display = new ActivityDisplay(); final TestActivityContainer container = new TestActivityContainer(service, stackId, display, onTop); - return container.getStack(); + mActivityContainers.put(stackId, container); + return (T) container.getStack(); + } + + @Override + protected <T extends ActivityStack> T getStack(int stackId, + boolean createStaticStackIfNeeded, boolean createOnTop) { + final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop); + + if (stack != null || !createStaticStackIfNeeded) { + return stack; + } + + return createTestStack(mService, stackId, createOnTop); } private class TestActivityContainer extends ActivityContainer { - private ActivityManagerService mService; - private TestActivityStack mStack; + private final ActivityManagerService mService; + private boolean mOnTop; + private int mStackId; + private ActivityStack mStack; TestActivityContainer(ActivityManagerService service, int stackId, ActivityDisplay activityDisplay, boolean onTop) { @@ -174,12 +218,16 @@ public class ActivityTestsBase { // we cannot set {@link mService} by the time the super constructor calling this // method is invoked. mOnTop = onTop; + mStackId = stackId; } - public TestActivityStack getStack() { + public ActivityStack getStack() { if (mStack == null) { - mStack = new TestActivityStack(this, - new RecentTasks(mService, mService.mStackSupervisor), mOnTop); + final RecentTasks recents = + new RecentTasks(mService, mService.mStackSupervisor); + mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID + ? new PinnedActivityStack(this, recents, mOnTop) + : new TestActivityStack(this, recents, mOnTop); } return mStack; @@ -187,13 +235,31 @@ public class ActivityTestsBase { } } + private static WindowManagerService prepareMockWindowManager() { + final WindowManagerService service = mock(WindowManagerService.class); + + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable runnable = invocationOnMock.<Runnable>getArgument(0); + if (runnable != null) { + runnable.run(); + } + return null; + }).when(service).inSurfaceTransaction(any()); + + return service; + } + + protected interface ActivityStackReporter { + int onActivityRemovedFromStackInvocationCount(); + } + /** * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a * method is called. Note that its functionality depends on the implementations of the * construction arguments. */ protected static class TestActivityStack<T extends StackWindowController> - extends ActivityStack<T> { + extends ActivityStack<T> implements ActivityStackReporter { private int mOnActivityRemovedFromStackCount = 0; private T mContainerController; TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, @@ -208,6 +274,7 @@ public class ActivityTestsBase { } // Returns the number of times {@link #onActivityRemovedFromStack} has been called + @Override public int onActivityRemovedFromStackInvocationCount() { return mOnActivityRemovedFromStackCount; } @@ -225,6 +292,7 @@ public class ActivityTestsBase { } } + protected static class ActivityStackBuilder { private boolean mOnTop = true; private int mStackId = 0; @@ -251,7 +319,7 @@ public class ActivityTestsBase { return this; } - public TestActivityStack build() { + public ActivityStack build() { return createActivityStack(mService, mStackId, mDisplayId, mOnTop); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index fbeda0a9191c..9392e8ee2b5c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -90,6 +90,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return null; }).when(am).notifyKeyguardFlagsChanged(any()); } + sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false, false, new TestWindowManagerPolicy()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java index 3a443575332e..ae3eb522837f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -48,6 +48,13 @@ public class WindowTestUtils { } /** + * Retrieves an instance of a mock {@link WindowManagerService}. + */ + public static WindowManagerService getMockWindowManagerService() { + return mock(WindowManagerService.class); + } + + /** * Creates a mock instance of {@link StackWindowController}. */ public static StackWindowController createMockStackWindowContainerController() { diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index 82b3792a331c..bf5c42b8ca1c 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.NetworkSpecifier; import android.net.wifi.RttManager; import android.util.Log; @@ -250,8 +251,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * unencrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -276,13 +277,13 @@ public class DiscoverySession { * request from only that peer. A RESPONDER may specify a {@code null} - * indicating that it will accept connection requests from any device. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) { + public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) { if (mTerminated) { Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session"); return null; @@ -302,8 +303,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -329,14 +330,14 @@ public class DiscoverySession { * {@link #createNetworkSpecifierOpen(PeerHandle)} API to * specify an open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle, - @NonNull String passphrase) { + public NetworkSpecifier createNetworkSpecifierPassphrase( + @Nullable PeerHandle peerHandle, @NonNull String passphrase) { if (passphrase == null || passphrase.length() == 0) { throw new IllegalArgumentException("Passphrase must not be null or empty"); } @@ -361,8 +362,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -389,8 +390,8 @@ public class DiscoverySession { * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an * open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. @@ -398,7 +399,7 @@ public class DiscoverySession { * @hide */ @SystemApi - public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, + public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, @NonNull byte[] pmk) { if (pmk == null || pmk.length == 0) { throw new IllegalArgumentException("PMK must not be null or empty"); diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 4d3957aece91..3fcbd4b60259 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -24,6 +24,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; +import android.net.NetworkSpecifier; import android.net.wifi.RttManager; import android.os.Binder; import android.os.Bundle; @@ -31,7 +32,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.util.Base64; import android.util.Log; import android.util.SparseArray; @@ -39,9 +39,6 @@ import com.android.internal.annotations.GuardedBy; import libcore.util.HexEncoding; -import org.json.JSONException; -import org.json.JSONObject; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -129,65 +126,6 @@ public class WifiAwareManager { private static final boolean VDBG = false; // STOPSHIP if true /** - * Keys used to generate a Network Specifier for the Aware network request. The network - * specifier is formatted as a JSON string. - */ - - /** - * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_IB = 0; - - /** - * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional - * [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; - - /** - * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; - - /** - * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional - * [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; - - - /** @hide */ - public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_TYPE = "type"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_ROLE = "role"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase"; - - /** * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed. * Use the {@link #isAvailable()} to query the current status. * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering @@ -483,7 +421,7 @@ public class WifiAwareManager { } /** @hide */ - public String createNetworkSpecifier(int clientId, int role, int sessionId, + public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId @@ -492,9 +430,6 @@ public class WifiAwareManager { + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); } - int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER - : NETWORK_SPECIFIER_TYPE_IB; - if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { throw new IllegalArgumentException( @@ -509,35 +444,20 @@ public class WifiAwareManager { } } - JSONObject json; - try { - json = new JSONObject(); - json.put(NETWORK_SPECIFIER_KEY_TYPE, type); - json.put(NETWORK_SPECIFIER_KEY_ROLE, role); - json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); - json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId); - if (peerHandle != null) { - json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId); - } - if (pmk == null) { - pmk = new byte[0]; - } - json.put(NETWORK_SPECIFIER_KEY_PMK, - Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); - if (passphrase == null) { - passphrase = new String(); - } - json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase); - - } catch (JSONException e) { - return ""; - } - - return json.toString(); + return new WifiAwareNetworkSpecifier( + (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER + : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, + role, + clientId, + sessionId, + peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID + null, // peerMac (not used in this method) + pmk, + passphrase); } /** @hide */ - public String createNetworkSpecifier(int clientId, @DataPathRole int role, + public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role, @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role @@ -545,9 +465,6 @@ public class WifiAwareManager { + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); } - int type = (peer == null) ? - NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB; - if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { throw new IllegalArgumentException( @@ -564,29 +481,16 @@ public class WifiAwareManager { throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); } - JSONObject json; - try { - json = new JSONObject(); - json.put(NETWORK_SPECIFIER_KEY_TYPE, type); - json.put(NETWORK_SPECIFIER_KEY_ROLE, role); - json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); - if (peer != null) { - json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer))); - } - if (pmk == null) { - pmk = new byte[0]; - } - json.put(NETWORK_SPECIFIER_KEY_PMK, - Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); - if (passphrase == null) { - passphrase = new String(); - } - json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase); - } catch (JSONException e) { - return ""; - } - - return json.toString(); + return new WifiAwareNetworkSpecifier( + (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER + : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, + role, + clientId, + 0, // 0 is an invalid session ID + 0, // 0 is an invalid peer ID + peer, + pmk, + passphrase); } private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java new file mode 100644 index 000000000000..59934806f398 --- /dev/null +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.aware; + +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects + * directly but obtain them using + * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or + * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase) + * versions. + * + * @hide + */ +public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { + /** + * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_IB = 0; + + /** + * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional + * [only permitted for RESPONDER] + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; + + /** + * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; + + /** + * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional + * [only permitted for RESPONDER] + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; + + /** @hide */ + public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; + + /** + * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object. + * @hide + */ + public final int type; + + /** + * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or + * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER. + * @hide + */ + public final int role; + + /** + * The client ID of the device. + * @hide + */ + public final int clientId; + + /** + * The session ID in which context to request a data-path. Only relevant for IB requests. + * @hide + */ + public final int sessionId; + + /** + * The peer ID of the device which the data-path should be connected to. Only relevant for + * IB requests (i.e. not IB_ANY_PEER or OOB*). + * @hide + */ + public final int peerId; + + /** + * The peer MAC address of the device which the data-path should be connected to. Only relevant + * for OB requests (i.e. not OOB_ANY_PEER or IB*). + * @hide + */ + public final byte[] peerMac; + + /** + * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should + * be specified. + * @hide + */ + public final byte[] pmk; + + /** + * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or + * passphrase should be specified. + * @hide + */ + public final String passphrase; + + /** @hide */ + public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, + byte[] peerMac, byte[] pmk, String passphrase) { + this.type = type; + this.role = role; + this.clientId = clientId; + this.sessionId = sessionId; + this.peerId = peerId; + this.peerMac = peerMac; + this.pmk = pmk; + this.passphrase = passphrase; + } + + public static final Creator<WifiAwareNetworkSpecifier> CREATOR = + new Creator<WifiAwareNetworkSpecifier>() { + @Override + public WifiAwareNetworkSpecifier createFromParcel(Parcel in) { + return new WifiAwareNetworkSpecifier( + in.readInt(), // type + in.readInt(), // role + in.readInt(), // clientId + in.readInt(), // sessionId + in.readInt(), // peerId + in.createByteArray(), // peerMac + in.createByteArray(), // pmk + in.readString()); // passphrase + } + + @Override + public WifiAwareNetworkSpecifier[] newArray(int size) { + return new WifiAwareNetworkSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(type); + dest.writeInt(role); + dest.writeInt(clientId); + dest.writeInt(sessionId); + dest.writeInt(peerId); + dest.writeByteArray(peerMac); + dest.writeByteArray(pmk); + dest.writeString(passphrase); + } + + /** @hide */ + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier. + return equals(other); + } + + /** @hide */ + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + type; + result = 31 * result + role; + result = 31 * result + clientId; + result = 31 * result + sessionId; + result = 31 * result + peerId; + result = 31 * result + Arrays.hashCode(peerMac); + result = 31 * result + Arrays.hashCode(pmk); + result = 31 * result + Objects.hashCode(passphrase); + + return result; + } + + /** @hide */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof WifiAwareNetworkSpecifier)) { + return false; + } + + WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj; + + return type == lhs.type + && role == lhs.role + && clientId == lhs.clientId + && sessionId == lhs.sessionId + && peerId == lhs.peerId + && Arrays.equals(peerMac, lhs.peerMac) + && Arrays.equals(pmk, lhs.pmk) + && Objects.equals(passphrase, lhs.passphrase); + } + + /** @hide */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier ["); + sb.append("type=").append(type) + .append(", role=").append(role) + .append(", clientId=").append(clientId) + .append(", sessionId=").append(sessionId) + .append(", peerId=").append(peerId) + // masking potential PII (although low impact information) + .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>") + // masking PII + .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>") + // masking PII + .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") + .append("]"); + return sb.toString(); + } +} diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 895defb9a540..ac3a6bba052c 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.NetworkSpecifier; import android.os.Binder; import android.os.Handler; import android.os.Looper; @@ -184,8 +185,8 @@ public class WifiAwareSession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * unencrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -205,29 +206,29 @@ public class WifiAwareSession { * peer. A RESPONDER may specify a {@code null} - indicating that it will accept * connection requests from any device. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer) { + public NetworkSpecifier createNetworkSpecifierOpen( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierOpen: called after termination"); - return ""; + return null; } return mgr.createNetworkSpecifier(mClientId, role, peer, null, null); } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -247,22 +248,23 @@ public class WifiAwareSession { * the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to * specify an open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer, @NonNull String passphrase) { + public NetworkSpecifier createNetworkSpecifierPassphrase( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, + @NonNull String passphrase) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination"); - return ""; + return null; } if (passphrase == null || passphrase.length() == 0) { throw new IllegalArgumentException("Passphrase must not be null or empty"); @@ -271,8 +273,8 @@ public class WifiAwareSession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -294,8 +296,8 @@ public class WifiAwareSession { * Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an * open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. @@ -303,16 +305,16 @@ public class WifiAwareSession { * @hide */ @SystemApi - public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer, @NonNull byte[] pmk) { + public NetworkSpecifier createNetworkSpecifierPmk( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierPmk: called after termination"); - return ""; + return null; } if (pmk == null || pmk.length == 0) { throw new IllegalArgumentException("PMK must not be null or empty"); diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 830db22929e8..72a6a7a7b46b 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -34,11 +34,9 @@ import android.os.IBinder; import android.os.Parcel; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Base64; import libcore.util.HexEncoding; -import org.json.JSONObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -959,8 +957,6 @@ public class WifiAwareManagerTest { final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); - String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); - ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor @@ -991,51 +987,37 @@ public class WifiAwareManagerTest { inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture()); // (3) request an open (unencrypted) network specifier from the session - String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle); + WifiAwareNetworkSpecifier ns = + (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierOpen( + peerHandle); // validate format - JSONObject jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); // (4) request an encrypted (PMK) network specifier from the session - networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk); + ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk( + peerHandle, pmk); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); - collector.checkThat("pmk", pmkB64 , - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); + collector.checkThat("pmk", pmk , equalTo(ns.pmk)); // (5) request an encrypted (Passphrase) network specifier from the session - networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle, - passphrase); + ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase( + peerHandle, passphrase); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); - collector.checkThat("passphrase", passphrase, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); + collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); @@ -1054,8 +1036,6 @@ public class WifiAwareManagerTest { final byte[] pmk = "Some arbitrary pmk data".getBytes(); final String passphrase = "A really bad password"; - String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); - ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor @@ -1074,47 +1054,32 @@ public class WifiAwareManagerTest { WifiAwareSession session = sessionCaptor.getValue(); // (2) request an open (unencrypted) direct network specifier - String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac); + WifiAwareNetworkSpecifier ns = + (WifiAwareNetworkSpecifier) session.createNetworkSpecifierOpen(role, someMac); // validate format - JSONObject jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); // (3) request an encrypted (PMK) direct network specifier - networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk); + ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPmk(role, someMac, pmk); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); - collector.checkThat("pmk", pmkB64, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); + collector.checkThat("pmk", pmk, equalTo(ns.pmk)); // (4) request an encrypted (Passphrase) direct network specifier - networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase); + ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPassphrase(role, someMac, + passphrase); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); - collector.checkThat("passphrase", passphrase, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); + collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); |