Merge "Fix toast lifetime"
diff --git a/Android.bp b/Android.bp
index ea1ed91..1b9210c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -498,7 +498,7 @@
"telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl",
"telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
"telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
- "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
@@ -683,7 +683,8 @@
// Loaded with System.loadLibrary by android.view.textclassifier
required: [
"libtextclassifier",
- "libmedia2_jni",],
+ "libmedia2_jni",
+ ],
javac_shard_size: 150,
@@ -833,7 +834,7 @@
" -I . " +
" $(in)",
- output_extension = "proto.h",
+ output_extension: "proto.h",
}
subdirs = [
diff --git a/Android.mk b/Android.mk
index 470714b..c4fffc89 100644
--- a/Android.mk
+++ b/Android.mk
@@ -664,6 +664,7 @@
-toroot / \
-hdf android.whichdoc online \
-devsite \
+ -yamlV2 \
$(sample_groups) \
-hdf android.hasSamples true \
-samplesdir $(samples_dir)
diff --git a/api/current.txt b/api/current.txt
index 45faf5d..addb48f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1549,6 +1549,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLayoutInDisplayCutoutMode = 16844167; // 0x1010587
field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
@@ -3883,6 +3884,7 @@
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+ method public boolean isBackgroundRestricted();
method public deprecated boolean isInLockTaskMode();
method public boolean isLowRamDevice();
method public static boolean isRunningInTestHarness();
@@ -7177,6 +7179,7 @@
field public static final java.lang.String HINT_ACTIONS = "actions";
field public static final java.lang.String HINT_CALLER_NEEDED = "caller_needed";
field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
+ field public static final java.lang.String HINT_KEY_WORDS = "key_words";
field public static final java.lang.String HINT_LARGE = "large";
field public static final java.lang.String HINT_LIST = "list";
field public static final java.lang.String HINT_LIST_ITEM = "list_item";
@@ -13592,7 +13595,7 @@
}
public class EmbossMaskFilter extends android.graphics.MaskFilter {
- ctor public EmbossMaskFilter(float[], float, float, float);
+ ctor public deprecated EmbossMaskFilter(float[], float, float, float);
}
public final class ImageDecoder implements java.lang.AutoCloseable {
@@ -14816,6 +14819,10 @@
method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
method public int describeContents();
+ method public int getResId();
+ method public java.lang.String getResPackage();
+ method public int getType();
+ method public android.net.Uri getUri();
method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
method public void loadDrawableAsync(android.content.Context, android.os.Message);
method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
@@ -14824,6 +14831,11 @@
method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
+ field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+ field public static final int TYPE_BITMAP = 1; // 0x1
+ field public static final int TYPE_DATA = 3; // 0x3
+ field public static final int TYPE_RESOURCE = 2; // 0x2
+ field public static final int TYPE_URI = 4; // 0x4
}
public static abstract interface Icon.OnDrawableLoadedListener {
@@ -22517,8 +22529,8 @@
method public java.io.FileDescriptor getFileDescriptor();
method public long getFileDescriptorLength();
method public long getFileDescriptorOffset();
- method public long getId();
method public android.media.Media2DataSource getMedia2DataSource();
+ method public java.lang.String getMediaId();
method public long getStartPosition();
method public int getType();
method public android.net.Uri getUri();
@@ -22542,7 +22554,7 @@
method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri);
method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>);
method public android.media.DataSourceDesc.Builder setEndPosition(long);
- method public android.media.DataSourceDesc.Builder setId(long);
+ method public android.media.DataSourceDesc.Builder setMediaId(java.lang.String);
method public android.media.DataSourceDesc.Builder setStartPosition(long);
}
@@ -22747,6 +22759,7 @@
method public abstract void close();
method public android.graphics.Rect getCropRect();
method public abstract int getFormat();
+ method public android.hardware.HardwareBuffer getHardwareBuffer();
method public abstract int getHeight();
method public abstract android.media.Image.Plane[] getPlanes();
method public abstract long getTimestamp();
@@ -23367,6 +23380,7 @@
method public void rewind();
method public void seekTo(long);
method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+ method public void setPlaybackSpeed(float);
method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
method public void setRating(java.lang.String, android.media.Rating2);
method public void setVolumeTo(int, int);
@@ -23385,13 +23399,13 @@
method public void onCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomLayoutChanged(java.util.List<android.media.MediaSession2.CommandButton>);
method public void onDisconnected();
- method public void onError(int, int);
+ method public void onError(int, android.os.Bundle);
method public void onPlaybackInfoChanged(android.media.MediaController2.PlaybackInfo);
method public void onPlaybackSpeedChanged(float);
method public void onPlayerStateChanged(int);
method public void onPlaylistChanged(java.util.List<android.media.MediaItem2>);
method public void onPlaylistParamsChanged(android.media.MediaSession2.PlaylistParams);
- method public void onPositionUpdated(long, long);
+ method public void onPositionChanged(long, long);
}
public static final class MediaController2.PlaybackInfo {
@@ -23430,6 +23444,11 @@
method protected void finalize();
method public boolean requiresSecureDecoderComponent(java.lang.String);
method public void setMediaCasSession(android.media.MediaCas.Session);
+ field public static final byte SCRAMBLE_CONTROL_EVEN_KEY = 2; // 0x2
+ field public static final byte SCRAMBLE_CONTROL_ODD_KEY = 3; // 0x3
+ field public static final byte SCRAMBLE_CONTROL_RESERVED = 1; // 0x1
+ field public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0; // 0x0
+ field public static final byte SCRAMBLE_FLAG_PES_HEADER = 1; // 0x1
}
public class MediaDescription implements android.os.Parcelable {
@@ -23843,9 +23862,11 @@
}
public static final class MediaLibraryService2.MediaLibrarySession.Builder {
- ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, android.media.MediaPlayerBase, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
+ ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
method public android.media.MediaLibraryService2.MediaLibrarySession build();
method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String);
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlayer(android.media.MediaPlayerBase);
+ method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistController(android.media.MediaPlaylistController);
method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
@@ -24297,68 +24318,79 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
- public abstract class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable {
- method public abstract void addPlaylistItem(int, android.media.DataSourceDesc);
+ public abstract class MediaPlayer2 extends android.media.MediaPlayerBase implements android.media.AudioRouting {
method public abstract void attachAuxEffect(int);
+ method public abstract void clearDrmEventCallback();
+ method public abstract void clearMediaPlayer2EventCallback();
method public abstract void clearPendingCommands();
method public abstract void close();
method public static final android.media.MediaPlayer2 create();
method public abstract void deselectTrack(int);
- method public abstract android.media.DataSourceDesc editPlaylistItem(int, android.media.DataSourceDesc);
- method public abstract android.media.AudioAttributes getAudioAttributes();
method public abstract int getAudioSessionId();
- method public abstract android.media.DataSourceDesc getCurrentDataSource();
- method public abstract int getCurrentPlaylistItemIndex();
+ method public abstract long getBufferedPosition();
method public abstract long getCurrentPosition();
method public abstract android.media.MediaPlayer2.DrmInfo getDrmInfo();
+ method public abstract android.media.MediaDrm.KeyRequest getDrmKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
method public abstract long getDuration();
- method public abstract android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract int getLoopingMode();
+ method public abstract int getMediaPlayer2State();
method public abstract android.os.PersistableBundle getMetrics();
method public abstract android.media.PlaybackParams getPlaybackParams();
- method public abstract java.util.List<android.media.DataSourceDesc> getPlaylist();
method public abstract int getSelectedTrack(int);
method public abstract android.media.SyncParams getSyncParams();
method public abstract android.media.MediaTimestamp getTimestamp();
method public abstract java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
method public abstract int getVideoHeight();
method public abstract int getVideoWidth();
- method public abstract boolean isPlaying();
- method public abstract void movePlaylistItem(int, int);
- method public abstract void pause();
- method public abstract void play();
- method public abstract void prepareAsync();
+ method public void notifyWhenCommandLabelReached(java.lang.Object);
method public abstract void prepareDrm(java.util.UUID) throws android.media.MediaPlayer2.ProvisioningNetworkErrorException, android.media.MediaPlayer2.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
- method public abstract byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void registerDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
- method public abstract void registerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.EventCallback);
+ method public abstract byte[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
method public abstract void releaseDrm() throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract android.media.DataSourceDesc removePlaylistItem(int);
method public abstract void reset();
- method public abstract void restoreKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
+ method public abstract void restoreDrmKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
+ method public void seekTo(long);
method public abstract void seekTo(long, int);
method public abstract void selectTrack(int);
- method public abstract void setAudioAttributes(android.media.AudioAttributes);
method public abstract void setAudioSessionId(int);
method public abstract void setAuxEffectSendLevel(float);
- method public abstract void setCurrentPlaylistItem(int);
- method public abstract void setDataSource(android.media.DataSourceDesc) throws java.io.IOException;
+ method public abstract void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
method public abstract void setDrmPropertyString(java.lang.String, java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void setLoopingMode(int);
- method public abstract void setNextPlaylistItem(int);
+ method public abstract void setMediaPlayer2EventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.MediaPlayer2EventCallback);
method public abstract void setOnDrmConfigHelper(android.media.MediaPlayer2.OnDrmConfigHelper);
method public abstract void setPlaybackParams(android.media.PlaybackParams);
- method public abstract void setPlaylist(java.util.List<android.media.DataSourceDesc>, int) throws java.io.IOException;
method public abstract void setSurface(android.view.Surface);
method public abstract void setSyncParams(android.media.SyncParams);
- method public abstract void setVolume(float, float);
- method public abstract void unregisterDrmEventCallback(android.media.MediaPlayer2.DrmEventCallback);
- method public abstract void unregisterEventCallback(android.media.MediaPlayer2.EventCallback);
- field public static final int LOOPING_MODE_FULL = 1; // 0x1
- field public static final int LOOPING_MODE_NONE = 0; // 0x0
- field public static final int LOOPING_MODE_SHUFFLE = 3; // 0x3
- field public static final int LOOPING_MODE_SINGLE = 2; // 0x2
+ field public static final int MEDIAPLAYER2_STATE_ERROR = 5; // 0x5
+ field public static final int MEDIAPLAYER2_STATE_IDLE = 1; // 0x1
+ field public static final int MEDIAPLAYER2_STATE_PAUSED = 3; // 0x3
+ field public static final int MEDIAPLAYER2_STATE_PLAYING = 4; // 0x4
+ field public static final int MEDIAPLAYER2_STATE_PREPARED = 2; // 0x2
+ field public static final int MEDIA_CALL_ATTACH_AUX_EFFECT = 1; // 0x1
+ field public static final int MEDIA_CALL_DESELECT_TRACK = 2; // 0x2
+ field public static final int MEDIA_CALL_LOOP_CURRENT = 3; // 0x3
+ field public static final int MEDIA_CALL_PAUSE = 4; // 0x4
+ field public static final int MEDIA_CALL_PLAY = 5; // 0x5
+ field public static final int MEDIA_CALL_PREPARE = 6; // 0x6
+ field public static final int MEDIA_CALL_PREPARE_DRM = 7; // 0x7
+ field public static final int MEDIA_CALL_PROVIDE_DRM_KEY_RESPONSE = 8; // 0x8
+ field public static final int MEDIA_CALL_RELEASE_DRM = 12; // 0xc
+ field public static final int MEDIA_CALL_RESTORE_DRM_KEYS = 13; // 0xd
+ field public static final int MEDIA_CALL_SEEK_TO = 14; // 0xe
+ field public static final int MEDIA_CALL_SELECT_TRACK = 15; // 0xf
+ field public static final int MEDIA_CALL_SET_AUDIO_ATTRIBUTES = 16; // 0x10
+ field public static final int MEDIA_CALL_SET_AUDIO_SESSION_ID = 17; // 0x11
+ field public static final int MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
+ field public static final int MEDIA_CALL_SET_DATA_SOURCE = 19; // 0x13
+ field public static final int MEDIA_CALL_SET_DRM_CONFIG_HELPER = 20; // 0x14
+ field public static final int MEDIA_CALL_SET_DRM_PROPERTY_STRING = 21; // 0x15
+ field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCE = 22; // 0x16
+ field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCES = 23; // 0x17
+ field public static final int MEDIA_CALL_SET_PLAYBACK_PARAMS = 24; // 0x18
+ field public static final int MEDIA_CALL_SET_PLAYBACK_SPEED = 25; // 0x19
+ field public static final int MEDIA_CALL_SET_PLAYER_VOLUME = 26; // 0x1a
+ field public static final int MEDIA_CALL_SET_SURFACE = 27; // 0x1b
+ field public static final int MEDIA_CALL_SET_SYNC_PARAMS = 28; // 0x1c
+ field public static final int MEDIA_CALL_SKIP_TO_NEXT = 29; // 0x1d
field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
@@ -24370,9 +24402,7 @@
field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
- field public static final int MEDIA_INFO_COMPLETE_CALL_PAUSE = 102; // 0x66
- field public static final int MEDIA_INFO_COMPLETE_CALL_PLAY = 101; // 0x65
- field public static final int MEDIA_INFO_COMPLETE_CALL_SEEK = 103; // 0x67
+ field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
field public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5; // 0x5
@@ -24398,8 +24428,8 @@
public static abstract class MediaPlayer2.DrmEventCallback {
ctor public MediaPlayer2.DrmEventCallback();
- method public void onDrmInfo(android.media.MediaPlayer2, long, android.media.MediaPlayer2.DrmInfo);
- method public void onDrmPrepared(android.media.MediaPlayer2, long, int);
+ method public void onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
+ method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int);
}
public static abstract class MediaPlayer2.DrmInfo {
@@ -24408,13 +24438,15 @@
method public abstract java.util.List<java.util.UUID> getSupportedSchemes();
}
- public static abstract class MediaPlayer2.EventCallback {
- ctor public MediaPlayer2.EventCallback();
- method public void onBufferingUpdate(android.media.MediaPlayer2, long, int);
- method public void onError(android.media.MediaPlayer2, long, int, int);
- method public void onInfo(android.media.MediaPlayer2, long, int, int);
- method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, long, android.media.TimedMetaData);
- method public void onVideoSizeChanged(android.media.MediaPlayer2, long, int, int);
+ public static abstract class MediaPlayer2.MediaPlayer2EventCallback {
+ ctor public MediaPlayer2.MediaPlayer2EventCallback();
+ method public void onCallComplete(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object);
+ method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onMediaTimeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp);
+ method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData);
+ method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
}
public static final class MediaPlayer2.MetricsConstants {
@@ -24437,7 +24469,7 @@
}
public static abstract interface MediaPlayer2.OnDrmConfigHelper {
- method public abstract void onDrmConfig(android.media.MediaPlayer2, long);
+ method public abstract void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc);
}
public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
@@ -24485,7 +24517,7 @@
method public abstract void setNextDataSource(android.media.DataSourceDesc);
method public abstract void setNextDataSources(java.util.List<android.media.DataSourceDesc>);
method public abstract void setPlaybackSpeed(float);
- method public abstract void setVolume(float);
+ method public abstract void setPlayerVolume(float);
method public abstract void skipToNext();
method public abstract void unregisterPlayerEventCallback(android.media.MediaPlayerBase.PlayerEventCallback);
field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
@@ -24795,15 +24827,17 @@
public class MediaSession2 implements java.lang.AutoCloseable android.media.MediaPlaylistController {
method public void addPlaylistItem(int, android.media.MediaItem2);
method public void close();
- method public void editPlaylistItem(android.media.MediaItem2);
method public void fastForward();
method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
method public android.media.MediaItem2 getCurrentPlaylistItem();
+ method public android.media.MediaPlaylistController getMediaPlaylistController();
+ method public float getPlaybackSpeed();
method public android.media.MediaPlayerBase getPlayer();
method public java.util.List<android.media.MediaItem2> getPlaylist();
method public android.media.MediaSession2.PlaylistParams getPlaylistParams();
method public android.media.SessionToken2 getToken();
- method public void notifyError(int, int);
+ method public android.media.VolumeProvider2 getVolumeProvider();
+ method public void notifyError(int, android.os.Bundle);
method public void pause();
method public void play();
method public void prepare();
@@ -24814,15 +24848,16 @@
method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
+ method public void setAudioFocusRequest(android.media.AudioFocusRequest);
method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
- method public void setPlayer(android.media.MediaPlayerBase);
- method public void setPlayer(android.media.MediaPlayerBase, android.media.VolumeProvider2);
+ method public void setPlaybackSpeed(float);
method public void setPlaylist(java.util.List<android.media.MediaItem2>);
method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
method public void skipToNext();
method public void skipToPlaylistItem(android.media.MediaItem2);
method public void skipToPrevious();
method public void stop();
+ method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistController, android.media.VolumeProvider2);
field public static final int COMMAND_CODE_BROWSER = 22; // 0x16
field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
@@ -24831,10 +24866,10 @@
field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
- field public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10; // 0xa
field public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 11; // 0xb
field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
+ field public static final int COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM = 10; // 0xa
field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
field public static final int COMMAND_CODE_PLAYLIST_ADD = 12; // 0xc
field public static final int COMMAND_CODE_PLAYLIST_GET = 14; // 0xe
@@ -24862,10 +24897,11 @@
}
public static final class MediaSession2.Builder {
- ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase);
- ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase, android.media.MediaPlaylistController);
+ ctor public MediaSession2.Builder(android.content.Context);
method public android.media.MediaSession2 build();
method public android.media.MediaSession2.Builder setId(java.lang.String);
+ method public android.media.MediaSession2.Builder setPlayer(android.media.MediaPlayerBase);
+ method public android.media.MediaSession2.Builder setPlaylistController(android.media.MediaPlaylistController);
method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
@@ -24876,13 +24912,13 @@
ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
method public int getCommandCode();
method public java.lang.String getCustomCommand();
- method public android.os.Bundle getExtra();
+ method public android.os.Bundle getExtras();
}
public static final class MediaSession2.CommandButton {
method public android.media.MediaSession2.Command getCommand();
method public java.lang.String getDisplayName();
- method public android.os.Bundle getExtra();
+ method public android.os.Bundle getExtras();
method public int getIconResId();
method public boolean isEnabled();
}
@@ -24893,7 +24929,7 @@
method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
- method public android.media.MediaSession2.CommandButton.Builder setExtra(android.os.Bundle);
+ method public android.media.MediaSession2.CommandButton.Builder setExtras(android.os.Bundle);
method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
}
@@ -24902,6 +24938,7 @@
ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
method public void addAllPredefinedCommands();
method public void addCommand(android.media.MediaSession2.Command);
+ method public java.util.List<android.media.MediaSession2.Command> getCommands();
method public boolean hasCommand(android.media.MediaSession2.Command);
method public boolean hasCommand(int);
method public void removeCommand(android.media.MediaSession2.Command);
@@ -25202,10 +25239,14 @@
method public android.media.AudioAttributes getAudioAttributes();
method public deprecated int getStreamType();
method public java.lang.String getTitle(android.content.Context);
+ method public float getVolume();
+ method public boolean isLooping();
method public boolean isPlaying();
method public void play();
method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+ method public void setLooping(boolean);
method public deprecated void setStreamType(int);
+ method public void setVolume(float);
method public void stop();
}
@@ -48782,6 +48823,7 @@
method public static deprecated int getWindowTouchSlop();
method public static long getZoomControlsTimeout();
method public boolean hasPermanentMenuKey();
+ method public boolean shouldShowMenuShortcutsWhenKeyboardPresent();
}
public class ViewDebug {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2f5af0d..0555263 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1592,7 +1592,7 @@
method public abstract void onMessageReceipt(int, int, android.hardware.location.ContextHubMessage);
}
- public deprecated class ContextHubMessage {
+ public deprecated class ContextHubMessage implements android.os.Parcelable {
ctor public ContextHubMessage(int, int, byte[]);
method public int describeContents();
method public byte[] getData();
@@ -1723,7 +1723,7 @@
field public static final android.os.Parcelable.Creator<android.hardware.location.MemoryRegion> CREATOR;
}
- public deprecated class NanoApp {
+ public deprecated class NanoApp implements android.os.Parcelable {
ctor public NanoApp();
ctor public deprecated NanoApp(int, byte[]);
ctor public NanoApp(long, byte[]);
@@ -1771,7 +1771,7 @@
field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppBinary> CREATOR;
}
- public deprecated class NanoAppFilter {
+ public deprecated class NanoAppFilter implements android.os.Parcelable {
ctor public NanoAppFilter(long, int, int, long);
method public int describeContents();
method public boolean testMatch(android.hardware.location.NanoAppInstanceInfo);
@@ -1786,7 +1786,7 @@
field public static final int VENDOR_ANY = -1; // 0xffffffff
}
- public deprecated class NanoAppInstanceInfo {
+ public deprecated class NanoAppInstanceInfo implements android.os.Parcelable {
ctor public NanoAppInstanceInfo();
method public int describeContents();
method public long getAppId();
@@ -2649,8 +2649,10 @@
}
public class AudioPolicy {
+ method public int attachMixes(java.util.List<android.media.audiopolicy.AudioMix>);
method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
+ method public int detachMixes(java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
method public int getStatus();
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -4310,7 +4312,8 @@
method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -5395,6 +5398,7 @@
field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2
field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1
field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4
+ field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
diff --git a/api/test-current.txt b/api/test-current.txt
index d5b4311..2559d24 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12,6 +12,7 @@
public class ActivityManager {
method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method public int getPackageImportance(java.lang.String);
+ method public long getTotalRam();
method public int getUidImportance(int);
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
@@ -47,7 +48,10 @@
public class AppOpsManager {
method public static java.lang.String[] getOpStrs();
+ method public boolean isOperationActive(int, int, java.lang.String);
method public void setMode(int, int, java.lang.String, int);
+ method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
+ method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
@@ -89,6 +93,12 @@
field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
+ field public static final int OP_RECORD_AUDIO = 27; // 0x1b
+ field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
+ }
+
+ public static abstract interface AppOpsManager.OnOpActiveChangedListener {
+ method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
}
public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -351,6 +361,7 @@
public final class DisplayManager {
method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+ method public android.graphics.Point getStableDisplaySize();
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
}
@@ -446,6 +457,10 @@
package android.os {
+ public static class Build.VERSION {
+ field public static final int RESOURCES_SDK_INT;
+ }
+
public class IncidentManager {
method public void reportIncident(android.os.IncidentReportArgs);
method public void reportIncident(java.lang.String, byte[]);
@@ -546,6 +561,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
+ field public static final java.lang.String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
field public static final java.lang.String LOW_POWER_MODE = "low_power";
field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index fc7cec9..c095f2b 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -94,10 +94,10 @@
// allocate extra 1 for NULL terminator
const char** ret = (const char**)malloc(sizeof(const char*) * (numOfArgs + 1));
ret[0] = first;
- for (int i = 0; i < numOfArgs; i++) {
+ for (int i = 1; i < numOfArgs; i++) {
const char* arg = va_arg(rest, const char*);
- ret[i + 1] = arg;
+ ret[i] = arg;
}
- ret[numOfArgs + 1] = NULL;
+ ret[numOfArgs] = NULL;
return ret;
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 7f76ab1..8ecb538 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -30,6 +30,7 @@
src/condition/condition_util.cpp \
src/condition/SimpleConditionTracker.cpp \
src/condition/ConditionWizard.cpp \
+ src/condition/StateTracker.cpp \
src/config/ConfigKey.cpp \
src/config/ConfigListener.cpp \
src/config/ConfigManager.cpp \
@@ -69,7 +70,6 @@
src/subscriber/IncidentdReporter.cpp \
src/subscriber/SubscriberReporter.cpp \
src/HashableDimensionKey.cpp \
- src/guardrail/MemoryLeakTrackUtil.cpp \
src/guardrail/StatsdStats.cpp
statsd_common_c_includes := \
@@ -102,8 +102,7 @@
android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
- android.hardware.thermal@1.0 \
- libmemunreachable
+ android.hardware.thermal@1.0
# =========
# statsd
@@ -131,7 +130,7 @@
LOCAL_CFLAGS += \
-Os
endif
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
@@ -189,6 +188,7 @@
tests/FieldValue_test.cpp \
tests/condition/CombinationConditionTracker_test.cpp \
tests/condition/SimpleConditionTracker_test.cpp \
+ tests/condition/StateTracker_test.cpp \
tests/metrics/OringDurationTracker_test.cpp \
tests/metrics/MaxDurationTracker_test.cpp \
tests/metrics/CountMetricProducer_test.cpp \
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b0e2c43..621d0be9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -41,6 +41,7 @@
inline int32_t getSimpleField(size_t field) {
return ((int32_t)field << 8 * 2);
}
+
/**
* Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
* proto.
@@ -201,9 +202,9 @@
* }
*
* We translate the FieldMatcher into a Field, and mask
- * First: [Matcher Field] 0x02010101 [Mask]0xffff7fff
- * Last: [Matcher Field] 0x02018001 [Mask]0xffff80ff
- * Any: [Matcher Field] 0x02010001 [Mask]0xffff00ff
+ * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f
+ * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f
+ * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f
*
* [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
* the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
@@ -235,9 +236,17 @@
inline bool operator!=(const Matcher& that) const {
return mMatcher != that.getMatcher() || mMask != that.getMask();
- };
+ }
+
+ inline bool operator==(const Matcher& that) const {
+ return mMatcher == that.mMatcher && mMask == that.mMask;
+ }
};
+inline Matcher getSimpleMatcher(int32_t tag, size_t field) {
+ return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
+}
+
/**
* A wrapper for a union type to contain multiple types of values.
*
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 087e596..9b58a14 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -74,7 +74,8 @@
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
mSendBroadcast(sendBroadcast),
- mTimeBaseSec(timeBaseSec) {
+ mTimeBaseSec(timeBaseSec),
+ mLastLogTimestamp(0) {
StatsPullerManager statsPullerManager;
statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
}
@@ -144,9 +145,12 @@
}
}
-// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ if (event->GetElapsedTimestampNs() < mLastLogTimestamp) {
+ return;
+ }
+ mLastLogTimestamp = event->GetElapsedTimestampNs();
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 4d9f185..7a6aa1e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -106,6 +106,8 @@
const long mTimeBaseSec;
+ int64_t mLastLogTimestamp;
+
long mLastPullerCacheClearTimeSec = 0;
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ee4f434..280081e 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -22,7 +22,6 @@
#include "android-base/stringprintf.h"
#include "config/ConfigKey.h"
#include "config/ConfigManager.h"
-#include "guardrail/MemoryLeakTrackUtil.h"
#include "guardrail/StatsdStats.h"
#include "storage/StorageManager.h"
#include "subscriber/SubscriberReporter.h"
@@ -638,9 +637,7 @@
}
status_t StatsService::cmd_dump_memory_info(FILE* out) {
- std::string s = dumpMemInfo(100);
- fprintf(out, "Memory Info\n");
- fprintf(out, "%s", s.c_str());
+ fprintf(out, "meminfo not available.\n");
return NO_ERROR;
}
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 642604e..6ee51f3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -193,7 +193,8 @@
if (!mSubscriptions.empty()) {
if (mAlert.has_id()) {
- ALOGI("An anomaly (%lld) has occurred! Informing subscribers.", mAlert.id());
+ ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", mAlert.id(),
+ key.toString().c_str());
informSubscribers(key);
} else {
ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers.");
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 42ae022..8e06504 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -104,6 +104,7 @@
KeyguardStateChanged keyguard_state_changed = 62;
KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
+ AppDied app_died=65;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -1235,6 +1236,17 @@
optional int64 swap_in_bytes = 8;
}
+/*
+ * Logs when the ActivityManagerService detects that an app died.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppDied {
+ // timestamp(elapsedRealtime) of record creation
+ optional uint64 timestamp_millis = 1;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index ba185f6..7b8dc6b 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -52,6 +52,29 @@
const vector<Matcher>& dimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ // Only one child predicate can have dimension.
+ const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const override {
+ for (const auto& child : mChildren) {
+ auto result = allConditions[child]->getChangedToTrueDimensions(allConditions);
+ if (result != nullptr) {
+ return result;
+ }
+ }
+ return nullptr;
+ }
+ // Only one child predicate can have dimension.
+ const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const override {
+ for (const auto& child : mChildren) {
+ auto result = allConditions[child]->getChangedToFalseDimensions(allConditions);
+ if (result != nullptr) {
+ return result;
+ }
+ }
+ return nullptr;
+ }
+
private:
LogicalOperation mLogicalOperation;
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 2612a9a..856a3a0 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -111,6 +111,11 @@
return mSliced;
}
+ virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
+ virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
+
protected:
const int64_t mConditionId;
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index c8722c3..952b0cc 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -41,6 +41,16 @@
*dimensionsKeySet);
}
+const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
+ const int index) const {
+ return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
+}
+
+const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions(
+ const int index) const {
+ return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4831d56..fcfdc2a 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -47,6 +47,10 @@
const int index, const vector<Matcher>& dimensionFields,
std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
+ virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
+ virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+ const int index) const;
+
private:
std::vector<sp<ConditionTracker>> mAllConditions;
};
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 624119f3..9e27a8b 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -20,8 +20,6 @@
#include "SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
-#include <log/logprint.h>
-
namespace android {
namespace os {
namespace statsd {
@@ -108,11 +106,20 @@
return mInitialized;
}
-void print(const map<HashableDimensionKey, int>& conditions, const int64_t& id) {
- VLOG("%lld DUMP:", (long long)id);
- for (const auto& pair : conditions) {
+void SimpleConditionTracker::dumpState() {
+ VLOG("%lld DUMP:", (long long)mConditionId);
+ for (const auto& pair : mSlicedConditionState) {
VLOG("\t%s : %d", pair.first.c_str(), pair.second);
}
+
+ VLOG("Changed to true keys: \n");
+ for (const auto& key : mLastChangedToTrueDimensions) {
+ VLOG("%s", key.toString().c_str());
+ }
+ VLOG("Changed to false keys: \n");
+ for (const auto& key : mLastChangedToFalseDimensions) {
+ VLOG("%s", key.toString().c_str());
+ }
}
void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
@@ -123,6 +130,12 @@
(mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
: true;
+ for (const auto& cond : mSlicedConditionState) {
+ if (cond.second > 0) {
+ mLastChangedToFalseDimensions.insert(cond.first);
+ }
+ }
+
// After StopAll, we know everything has stopped. From now on, default condition is false.
mInitialValue = ConditionState::kFalse;
mSlicedConditionState.clear();
@@ -166,10 +179,12 @@
if (matchStart && mInitialValue != ConditionState::kTrue) {
mSlicedConditionState.insert(std::make_pair(outputKey, 1));
changed = true;
+ mLastChangedToTrueDimensions.insert(outputKey);
} else if (mInitialValue != ConditionState::kFalse) {
// it's a stop and we don't have history about it.
// If the default condition is not false, it means this stop is valuable to us.
mSlicedConditionState.insert(std::make_pair(outputKey, 0));
+ mLastChangedToFalseDimensions.insert(outputKey);
changed = true;
}
} else {
@@ -179,6 +194,7 @@
newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (matchStart) {
if (startedCount == 0) {
+ mLastChangedToTrueDimensions.insert(outputKey);
// This condition for this output key will change from false -> true
changed = true;
}
@@ -202,6 +218,7 @@
}
// if everything has stopped for this output key, condition true -> false;
if (startedCount == 0) {
+ mLastChangedToFalseDimensions.insert(outputKey);
changed = true;
}
}
@@ -216,7 +233,7 @@
// dump all dimensions for debugging
if (DEBUG) {
- print(mSlicedConditionState, mConditionId);
+ dumpState();
}
(*conditionChangedCache) = changed;
@@ -237,6 +254,8 @@
(long long)mConditionId, conditionCache[mIndex]);
return;
}
+ mLastChangedToTrueDimensions.clear();
+ mLastChangedToFalseDimensions.clear();
if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
@@ -297,14 +316,14 @@
// A high level assumption is that a predicate is either sliced or unsliced. We will never
// have both sliced and unsliced version of a predicate.
for (const HashableDimensionKey& outputValue : outputValues) {
- // For sliced conditions, the value in the cache is not used. We don't need to update
- // the overall condition state.
- ConditionState tempState = ConditionState::kUnknown;
+ ConditionState tempState;
bool tempChanged = false;
handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged);
if (tempChanged) {
overallChanged = true;
}
+ // ConditionState's | operator is overridden
+ overallState = overallState | tempState;
}
}
conditionCache[mIndex] = overallState;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index c565129..e4b72b8 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -57,6 +57,23 @@
const vector<Matcher>& dimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const {
+ if (mSliced) {
+ return &mLastChangedToTrueDimensions;
+ } else {
+ return nullptr;
+ }
+ }
+ virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const {
+ if (mSliced) {
+ return &mLastChangedToFalseDimensions;
+ } else {
+ return nullptr;
+ }
+ }
+
private:
const ConfigKey mConfigKey;
// The index of the LogEventMatcher which defines the start.
@@ -75,6 +92,9 @@
std::vector<Matcher> mOutputDimensions;
+ std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
+ std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
+
int mDimensionTag;
std::map<HashableDimensionKey, int> mSlicedConditionState;
@@ -87,6 +107,8 @@
bool hitGuardRail(const HashableDimensionKey& newKey);
+ void dumpState();
+
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
new file mode 100644
index 0000000..e479f93
--- /dev/null
+++ b/cmds/statsd/src/condition/StateTracker.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StateTracker.h"
+#include "guardrail/StatsdStats.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+ const SimplePredicate& simplePredicate,
+ const unordered_map<int64_t, int>& trackerNameIndexMap,
+ const vector<Matcher> primaryKeys)
+ : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) {
+ if (simplePredicate.has_start()) {
+ auto pair = trackerNameIndexMap.find(simplePredicate.start());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+ return;
+ }
+ mStartLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStartLogMatcherIndex);
+ } else {
+ ALOGW("Condition %lld must have a start matcher", (long long)id);
+ return;
+ }
+
+ if (simplePredicate.has_dimensions()) {
+ translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
+ if (mOutputDimensions.size() > 0) {
+ mSliced = true;
+ mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
+ } else {
+ ALOGW("Condition %lld has invalid dimensions", (long long)id);
+ return;
+ }
+ } else {
+ ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id);
+ return;
+ }
+
+ if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
+ mInitialValue = ConditionState::kFalse;
+ } else {
+ mInitialValue = ConditionState::kUnknown;
+ }
+
+ mNonSlicedConditionState = mInitialValue;
+ mInitialized = true;
+}
+
+StateTracker::~StateTracker() {
+ VLOG("~StateTracker()");
+}
+
+bool StateTracker::init(const vector<Predicate>& allConditionConfig,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionIdIndexMap,
+ vector<bool>& stack) {
+ return mInitialized;
+}
+
+void StateTracker::dumpState() {
+ VLOG("StateTracker %lld DUMP:", (long long)mConditionId);
+ for (const auto& value : mSlicedState) {
+ VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
+ }
+ VLOG("Last Changed to True: ");
+ for (const auto& value : mLastChangedToTrueDimensions) {
+ VLOG("%s", value.toString().c_str());
+ }
+ VLOG("Last Changed to False: ");
+ for (const auto& value : mLastChangedToFalseDimensions) {
+ VLOG("%s", value.toString().c_str());
+ }
+}
+
+bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+ if (mSlicedState.find(newKey) != mSlicedState.end()) {
+ // if the condition is not sliced or the key is not new, we are good!
+ return false;
+ }
+ // 1. Report the tuple count if the tuple count > soft limit
+ if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ size_t newTupleCount = mSlicedState.size() + 1;
+ StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
+ // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+ if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+ ALOGE("Predicate %lld dropping data for dimension key %s",
+ (long long)mConditionId, newKey.c_str());
+ return true;
+ }
+ }
+ return false;
+}
+
+void StateTracker::evaluateCondition(const LogEvent& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& conditionChangedCache) {
+ mLastChangedToTrueDimensions.clear();
+ mLastChangedToFalseDimensions.clear();
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
+ return;
+ }
+
+ if (mStartLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) {
+ conditionCache[mIndex] =
+ mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionChangedCache[mIndex] = false;
+ return;
+ }
+
+ VLOG("StateTracker evaluate event %s", event.ToString().c_str());
+
+ vector<HashableDimensionKey> keys;
+ vector<HashableDimensionKey> outputs;
+ filterValues(mPrimaryKeys, event.getValues(), &keys);
+ filterValues(mOutputDimensions, event.getValues(), &outputs);
+ if (keys.size() != 1 || outputs.size() != 1) {
+ ALOGE("More than 1 states in the event?? panic now!");
+ conditionCache[mIndex] =
+ mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionChangedCache[mIndex] = false;
+ return;
+ }
+ // Primary key can exclusive fields must be simple fields. so there won't be more than
+ // one keys matched.
+ const auto& primaryKey = keys[0];
+ const auto& state = outputs[0];
+ hitGuardRail(primaryKey);
+
+ VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
+
+ auto it = mSlicedState.find(primaryKey);
+ if (it == mSlicedState.end()) {
+ mSlicedState[primaryKey] = state;
+ conditionCache[mIndex] = ConditionState::kTrue;
+ mLastChangedToTrueDimensions.insert(state);
+ conditionChangedCache[mIndex] = true;
+ } else if (!(it->second == state)) {
+ mLastChangedToFalseDimensions.insert(it->second);
+ mLastChangedToTrueDimensions.insert(state);
+ mSlicedState[primaryKey] = state;
+ conditionCache[mIndex] = ConditionState::kTrue;
+ conditionChangedCache[mIndex] = true;
+ } else {
+ conditionCache[mIndex] = ConditionState::kTrue;
+ conditionChangedCache[mIndex] = false;
+ }
+
+ if (DEBUG) {
+ dumpState();
+ }
+ return;
+}
+
+void StateTracker::isConditionMet(
+ const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
+ const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
+ return;
+ }
+
+ const auto pair = conditionParameters.find(mConditionId);
+ if (pair == conditionParameters.end()) {
+ if (mSlicedState.size() > 0) {
+ conditionCache[mIndex] = ConditionState::kTrue;
+
+ for (const auto& state : mSlicedState) {
+ dimensionsKeySet.insert(state.second);
+ }
+ } else {
+ conditionCache[mIndex] = ConditionState::kUnknown;
+ }
+ return;
+ }
+
+ const auto& primaryKeys = pair->second;
+ conditionCache[mIndex] = mInitialValue;
+ for (const auto& primaryKey : primaryKeys) {
+ auto it = mSlicedState.find(primaryKey);
+ if (it != mSlicedState.end()) {
+ conditionCache[mIndex] = ConditionState::kTrue;
+ dimensionsKeySet.insert(it->second);
+ }
+ }
+}
+
+ConditionState StateTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const vector<Matcher>& dimensionFields,
+ std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ if (mSlicedState.size() > 0) {
+ for (const auto& state : mSlicedState) {
+ dimensionsKeySet.insert(state.second);
+ }
+ return ConditionState::kTrue;
+ }
+
+ return mInitialValue;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h
new file mode 100644
index 0000000..3fe6e60
--- /dev/null
+++ b/cmds/statsd/src/condition/StateTracker.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "ConditionTracker.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual ConditionTracker {
+public:
+ StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+ const SimplePredicate& simplePredicate,
+ const std::unordered_map<int64_t, int>& trackerNameIndexMap,
+ const vector<Matcher> primaryKeys);
+
+ ~StateTracker();
+
+ bool init(const std::vector<Predicate>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap,
+ std::vector<bool>& stack) override;
+
+ void evaluateCondition(const LogEvent& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) override;
+
+ /**
+ * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
+ * must take the entire dimension fields from StateTracker. This is to make implementation
+ * simple and efficient.
+ *
+ * For example: wakelock duration by uid process states:
+ * dimension in condition must be {uid, process state}.
+ */
+ void isConditionMet(const ConditionKey& conditionParameters,
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const vector<Matcher>& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+
+ /**
+ * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
+ * must take the entire dimension fields from StateTracker. This is to make implementation
+ * simple and efficient.
+ */
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const vector<Matcher>& dimensionFields,
+ std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+
+ virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const {
+ return &mLastChangedToTrueDimensions;
+ }
+
+ virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+ const std::vector<sp<ConditionTracker>>& allConditions) const {
+ return &mLastChangedToFalseDimensions;
+ }
+
+private:
+ const ConfigKey mConfigKey;
+
+ // The index of the LogEventMatcher which defines the start.
+ int mStartLogMatcherIndex;
+
+ std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
+ std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
+
+ std::vector<Matcher> mOutputDimensions;
+ std::vector<Matcher> mPrimaryKeys;
+
+ ConditionState mInitialValue;
+
+ int mDimensionTag;
+
+ void dumpState();
+
+ bool hitGuardRail(const HashableDimensionKey& newKey);
+
+ // maps from [primary_key] to [primary_key, exclusive_state].
+ std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
+
+ FRIEND_TEST(StateTrackerTest, TestStateChange);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp
index d791f86..4a2bd27 100644
--- a/cmds/statsd/src/config/ConfigKey.cpp
+++ b/cmds/statsd/src/config/ConfigKey.cpp
@@ -16,14 +16,10 @@
#include "config/ConfigKey.h"
-#include <sstream>
-
namespace android {
namespace os {
namespace statsd {
-using std::ostringstream;
-
ConfigKey::ConfigKey() {
}
@@ -37,9 +33,9 @@
}
string ConfigKey::ToString() const {
- ostringstream out;
- out << '(' << mUid << ',' << mId << ')';
- return out.str();
+ string s;
+ s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")";
+ return s;
}
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index 3ad0eed..dc79519 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -18,8 +18,6 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include <functional>
-#include <iostream>
#include <string>
namespace android {
@@ -27,7 +25,6 @@
namespace statsd {
using std::hash;
-using std::ostream;
using std::string;
/**
@@ -68,10 +65,6 @@
int mUid;
};
-inline ostream& operator<<(ostream& os, const ConfigKey& config) {
- return os << config.ToString();
-}
-
int64_t StrToInt64(const string& str);
} // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 0881d44..22faaa1 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -36,6 +36,7 @@
using android::util::ProtoOutputStream;
using std::lock_guard;
using std::map;
+using std::shared_ptr;
using std::string;
using std::vector;
@@ -58,6 +59,38 @@
const int FIELD_ID_LOGGER_STATS_TIME = 1;
const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
+const int FIELD_ID_CONFIG_STATS_UID = 1;
+const int FIELD_ID_CONFIG_STATS_ID = 2;
+const int FIELD_ID_CONFIG_STATS_CREATION = 3;
+const int FIELD_ID_CONFIG_STATS_DELETION = 4;
+const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5;
+const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6;
+const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7;
+const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8;
+const int FIELD_ID_CONFIG_STATS_VALID = 9;
+const int FIELD_ID_CONFIG_STATS_BROADCAST = 10;
+const int FIELD_ID_CONFIG_STATS_DATA_DROP = 11;
+const int FIELD_ID_CONFIG_STATS_DUMP_REPORT = 12;
+const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13;
+const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14;
+const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15;
+const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16;
+
+const int FIELD_ID_MATCHER_STATS_ID = 1;
+const int FIELD_ID_MATCHER_STATS_COUNT = 2;
+const int FIELD_ID_CONDITION_STATS_ID = 1;
+const int FIELD_ID_CONDITION_STATS_COUNT = 2;
+const int FIELD_ID_METRIC_STATS_ID = 1;
+const int FIELD_ID_METRIC_STATS_COUNT = 2;
+const int FIELD_ID_ALERT_STATS_ID = 1;
+const int FIELD_ID_ALERT_STATS_COUNT = 2;
+
+const int FIELD_ID_UID_MAP_SNAPSHOTS = 1;
+const int FIELD_ID_UID_MAP_CHANGES = 2;
+const int FIELD_ID_UID_MAP_BYTES_USED = 3;
+const int FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS = 4;
+const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 5;
+
std::map<int, long> StatsdStats::kPullerCooldownMap = {
{android::util::KERNEL_WAKELOCK, 1},
{android::util::WIFI_BYTES_TRANSFER, 1},
@@ -81,7 +114,7 @@
return statsInstance;
}
-void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) {
+void StatsdStats::addToIceBoxLocked(shared_ptr<ConfigStats>& stats) {
// The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize.
if (mIceBox.size() == kMaxIceBoxSize) {
mIceBox.pop_front();
@@ -97,20 +130,20 @@
// If there is an existing config for the same key, icebox the old config.
noteConfigRemovedInternalLocked(key);
- StatsdStatsReport_ConfigStats configStats;
- configStats.set_uid(key.GetUid());
- configStats.set_id(key.GetId());
- configStats.set_creation_time_sec(nowTimeSec);
- configStats.set_metric_count(metricsCount);
- configStats.set_condition_count(conditionsCount);
- configStats.set_matcher_count(matchersCount);
- configStats.set_alert_count(alertsCount);
- configStats.set_is_valid(isValid);
+ shared_ptr<ConfigStats> configStats = std::make_shared<ConfigStats>();
+ configStats->uid = key.GetUid();
+ configStats->id = key.GetId();
+ configStats->creation_time_sec = nowTimeSec;
+ configStats->metric_count = metricsCount;
+ configStats->condition_count = conditionsCount;
+ configStats->matcher_count = matchersCount;
+ configStats->alert_count = alertsCount;
+ configStats->is_valid = isValid;
if (isValid) {
mConfigStats[key] = configStats;
} else {
- configStats.set_deletion_time_sec(nowTimeSec);
+ configStats->deletion_time_sec = nowTimeSec;
addToIceBoxLocked(configStats);
}
}
@@ -119,14 +152,7 @@
auto it = mConfigStats.find(key);
if (it != mConfigStats.end()) {
int32_t nowTimeSec = getWallClockSec();
- it->second.set_deletion_time_sec(nowTimeSec);
- // Add condition stats, metrics stats, matcher stats, alert stats
- addSubStatsToConfigLocked(key, it->second);
- // Remove them after they are added to the config stats.
- mMatcherStats.erase(key);
- mMetricsStats.erase(key);
- mAlertStats.erase(key);
- mConditionStats.erase(key);
+ it->second->deletion_time_sec = nowTimeSec;
addToIceBoxLocked(it->second);
mConfigStats.erase(it);
}
@@ -148,12 +174,10 @@
ALOGE("Config key %s not found!", key.ToString().c_str());
return;
}
- if (it->second.broadcast_sent_time_sec_size() >= kMaxTimestampCount) {
- auto timestampList = it->second.mutable_broadcast_sent_time_sec();
- // This is O(N) operation. It shouldn't happen often, and N is only 20.
- timestampList->erase(timestampList->begin());
+ if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) {
+ it->second->broadcast_sent_time_sec.pop_front();
}
- it->second.add_broadcast_sent_time_sec(timeSec);
+ it->second->broadcast_sent_time_sec.push_back(timeSec);
}
void StatsdStats::noteDataDropped(const ConfigKey& key) {
@@ -167,12 +191,10 @@
ALOGE("Config key %s not found!", key.ToString().c_str());
return;
}
- if (it->second.data_drop_time_sec_size() >= kMaxTimestampCount) {
- auto timestampList = it->second.mutable_data_drop_time_sec();
- // This is O(N) operation. It shouldn't happen often, and N is only 20.
- timestampList->erase(timestampList->begin());
+ if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) {
+ it->second->data_drop_time_sec.pop_front();
}
- it->second.add_data_drop_time_sec(timeSec);
+ it->second->data_drop_time_sec.push_back(timeSec);
}
void StatsdStats::noteMetricsReportSent(const ConfigKey& key) {
@@ -186,39 +208,42 @@
ALOGE("Config key %s not found!", key.ToString().c_str());
return;
}
- if (it->second.dump_report_time_sec_size() >= kMaxTimestampCount) {
- auto timestampList = it->second.mutable_dump_report_time_sec();
- // This is O(N) operation. It shouldn't happen often, and N is only 20.
- timestampList->erase(timestampList->begin());
+ if (it->second->dump_report_time_sec.size() == kMaxTimestampCount) {
+ it->second->dump_report_time_sec.pop_front();
}
- it->second.add_dump_report_time_sec(timeSec);
+ it->second->dump_report_time_sec.push_back(timeSec);
}
void StatsdStats::noteUidMapDropped(int snapshots, int deltas) {
lock_guard<std::mutex> lock(mLock);
- mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots);
- mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas);
+ mUidMapStats.dropped_snapshots += mUidMapStats.dropped_snapshots + snapshots;
+ mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas;
}
void StatsdStats::setUidMapSnapshots(int snapshots) {
lock_guard<std::mutex> lock(mLock);
- mUidMapStats.set_snapshots(snapshots);
+ mUidMapStats.snapshots = snapshots;
}
void StatsdStats::setUidMapChanges(int changes) {
lock_guard<std::mutex> lock(mLock);
- mUidMapStats.set_changes(changes);
+ mUidMapStats.changes = changes;
}
void StatsdStats::setCurrentUidMapMemory(int bytes) {
lock_guard<std::mutex> lock(mLock);
- mUidMapStats.set_bytes_used(bytes);
+ mUidMapStats.bytes_used = bytes;
}
void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
- auto& conditionSizeMap = mConditionStats[key];
+ auto statsIt = mConfigStats.find(key);
+ if (statsIt == mConfigStats.end()) {
+ return;
+ }
+
+ auto& conditionSizeMap = statsIt->second->condition_stats;
if (size > conditionSizeMap[id]) {
conditionSizeMap[id] = size;
}
@@ -227,7 +252,11 @@
void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
- auto& metricsDimensionMap = mMetricsStats[key];
+ auto statsIt = mConfigStats.find(key);
+ if (statsIt == mConfigStats.end()) {
+ return;
+ }
+ auto& metricsDimensionMap = statsIt->second->metric_stats;
if (size > metricsDimensionMap[id]) {
metricsDimensionMap[id] = size;
}
@@ -235,14 +264,21 @@
void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
lock_guard<std::mutex> lock(mLock);
- auto& matcherStats = mMatcherStats[key];
- matcherStats[id]++;
+
+ auto statsIt = mConfigStats.find(key);
+ if (statsIt == mConfigStats.end()) {
+ return;
+ }
+ statsIt->second->matcher_stats[id]++;
}
void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
lock_guard<std::mutex> lock(mLock);
- auto& alertStats = mAlertStats[key];
- alertStats[id]++;
+ auto statsIt = mConfigStats.find(key);
+ if (statsIt == mConfigStats.end()) {
+ return;
+ }
+ statsIt->second->alert_stats[id]++;
}
void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
@@ -299,69 +335,18 @@
// Reset the historical data, but keep the active ConfigStats
mStartTimeSec = getWallClockSec();
mIceBox.clear();
- mConditionStats.clear();
- mMetricsStats.clear();
std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
- mAlertStats.clear();
mAnomalyAlarmRegisteredStats = 0;
mPeriodicAlarmRegisteredStats = 0;
- mMatcherStats.clear();
mLoggerErrors.clear();
for (auto& config : mConfigStats) {
- config.second.clear_broadcast_sent_time_sec();
- config.second.clear_data_drop_time_sec();
- config.second.clear_dump_report_time_sec();
- config.second.clear_matcher_stats();
- config.second.clear_condition_stats();
- config.second.clear_metric_stats();
- config.second.clear_alert_stats();
- }
-}
-
-void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key,
- StatsdStatsReport_ConfigStats& configStats) {
- // Add matcher stats
- if (mMatcherStats.find(key) != mMatcherStats.end()) {
- const auto& matcherStats = mMatcherStats[key];
- for (const auto& stats : matcherStats) {
- auto output = configStats.add_matcher_stats();
- output->set_id(stats.first);
- output->set_matched_times(stats.second);
- VLOG("matcher %lld matched %d times",
- (long long)stats.first, stats.second);
- }
- }
- // Add condition stats
- if (mConditionStats.find(key) != mConditionStats.end()) {
- const auto& conditionStats = mConditionStats[key];
- for (const auto& stats : conditionStats) {
- auto output = configStats.add_condition_stats();
- output->set_id(stats.first);
- output->set_max_tuple_counts(stats.second);
- VLOG("condition %lld max output tuple size %d",
- (long long)stats.first, stats.second);
- }
- }
- // Add metrics stats
- if (mMetricsStats.find(key) != mMetricsStats.end()) {
- const auto& conditionStats = mMetricsStats[key];
- for (const auto& stats : conditionStats) {
- auto output = configStats.add_metric_stats();
- output->set_id(stats.first);
- output->set_max_tuple_counts(stats.second);
- VLOG("metrics %lld max output tuple size %d",
- (long long)stats.first, stats.second);
- }
- }
- // Add anomaly detection alert stats
- if (mAlertStats.find(key) != mAlertStats.end()) {
- const auto& alertStats = mAlertStats[key];
- for (const auto& stats : alertStats) {
- auto output = configStats.add_alert_stats();
- output->set_id(stats.first);
- output->set_alerted_times(stats.second);
- VLOG("alert %lld declared %d times", (long long)stats.first, stats.second);
- }
+ config.second->broadcast_sent_time_sec.clear();
+ config.second->data_drop_time_sec.clear();
+ config.second->dump_report_time_sec.clear();
+ config.second->matcher_stats.clear();
+ config.second->condition_stats.clear();
+ config.second->metric_stats.clear();
+ config.second->alert_stats.clear();
}
}
@@ -377,78 +362,57 @@
fprintf(out,
"Config {%d_%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
- configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
- configStats.deletion_time_sec(), configStats.metric_count(),
- configStats.condition_count(), configStats.matcher_count(),
- configStats.alert_count(), configStats.is_valid());
+ configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
+ configStats->deletion_time_sec, configStats->metric_count,
+ configStats->condition_count, configStats->matcher_count, configStats->alert_count,
+ configStats->is_valid);
- for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+ for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
}
- for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+ for (const auto& dataDropTime : configStats->data_drop_time_sec) {
fprintf(out, "\tdata drop time: %d\n", dataDropTime);
}
}
fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
for (auto& pair : mConfigStats) {
- auto& key = pair.first;
auto& configStats = pair.second;
-
fprintf(out,
"Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
- configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
- configStats.deletion_time_sec(), configStats.metric_count(),
- configStats.condition_count(), configStats.matcher_count(),
- configStats.alert_count(), configStats.is_valid());
- for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+ configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
+ configStats->deletion_time_sec, configStats->metric_count,
+ configStats->condition_count, configStats->matcher_count, configStats->alert_count,
+ configStats->is_valid);
+ for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
}
- for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+ for (const auto& dataDropTime : configStats->data_drop_time_sec) {
fprintf(out, "\tdata drop time: %d\n", dataDropTime);
}
- for (const auto& dumpTime : configStats.dump_report_time_sec()) {
+ for (const auto& dumpTime : configStats->dump_report_time_sec) {
fprintf(out, "\tdump report time: %d\n", dumpTime);
}
- // Add matcher stats
- auto matcherIt = mMatcherStats.find(key);
- if (matcherIt != mMatcherStats.end()) {
- const auto& matcherStats = matcherIt->second;
- for (const auto& stats : matcherStats) {
- fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first,
- stats.second);
- }
+ for (const auto& stats : pair.second->matcher_stats) {
+ fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
}
- // Add condition stats
- auto conditionIt = mConditionStats.find(key);
- if (conditionIt != mConditionStats.end()) {
- const auto& conditionStats = conditionIt->second;
- for (const auto& stats : conditionStats) {
- fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
- stats.second);
- }
+
+ for (const auto& stats : pair.second->condition_stats) {
+ fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
+ stats.second);
}
- // Add metrics stats
- auto metricIt = mMetricsStats.find(key);
- if (metricIt != mMetricsStats.end()) {
- const auto& conditionStats = metricIt->second;
- for (const auto& stats : conditionStats) {
- fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
- stats.second);
- }
+
+ for (const auto& stats : pair.second->condition_stats) {
+ fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
+ stats.second);
}
- // Add anomaly detection alert stats
- auto alertIt = mAlertStats.find(key);
- if (alertIt != mAlertStats.end()) {
- const auto& alertStats = alertIt->second;
- for (const auto& stats : alertStats) {
- fprintf(out, "alert %lld declared %d times\n", (long long)stats.first,
- stats.second);
- }
+
+ for (const auto& stats : pair.second->alert_stats) {
+ fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
}
}
fprintf(out, "********Pushed Atom stats***********\n");
@@ -462,7 +426,7 @@
fprintf(out, "********Pulled Atom stats***********\n");
for (const auto& pair : mPulledAtomStats) {
fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
- (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
+ (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
}
if (mAnomalyAlarmRegisteredStats > 0) {
@@ -478,8 +442,8 @@
fprintf(out,
"UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
"lost=%d\n",
- mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
- mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
+ mUidMapStats.bytes_used, mUidMapStats.snapshots, mUidMapStats.changes,
+ mUidMapStats.dropped_snapshots, mUidMapStats.dropped_changes);
for (const auto& error : mLoggerErrors) {
time_t error_time = error.first;
@@ -490,6 +454,73 @@
}
}
+void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) {
+ long long token =
+ proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec);
+ if (configStats.deletion_time_sec != 0) {
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION,
+ configStats.deletion_time_sec);
+ }
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT,
+ configStats.condition_count);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count);
+ proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid);
+
+ for (const auto& broadcast : configStats.broadcast_sent_time_sec) {
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED,
+ broadcast);
+ }
+
+ for (const auto& drop : configStats.data_drop_time_sec) {
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP | FIELD_COUNT_REPEATED,
+ drop);
+ }
+
+ for (const auto& dump : configStats.dump_report_time_sec) {
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT | FIELD_COUNT_REPEATED,
+ dump);
+ }
+
+ for (const auto& pair : configStats.matcher_stats) {
+ long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_CONFIG_STATS_MATCHER_STATS);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second);
+ proto->end(tmpToken);
+ }
+
+ for (const auto& pair : configStats.condition_stats) {
+ long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_CONFIG_STATS_CONDITION_STATS);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second);
+ proto->end(tmpToken);
+ }
+
+ for (const auto& pair : configStats.metric_stats) {
+ long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_CONFIG_STATS_METRIC_STATS);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
+ proto->end(tmpToken);
+ }
+
+ for (const auto& pair : configStats.alert_stats) {
+ long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_CONFIG_STATS_ALERT_STATS);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second);
+ proto->end(tmpToken);
+ }
+
+ proto->end(token);
+}
+
void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
lock_guard<std::mutex> lock(mLock);
@@ -498,28 +529,11 @@
proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec());
for (const auto& configStats : mIceBox) {
- const int numBytes = configStats.ByteSize();
- vector<char> buffer(numBytes);
- configStats.SerializeToArray(&buffer[0], numBytes);
- proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
- buffer.size());
+ addConfigStatsToProto(*configStats, &proto);
}
for (auto& pair : mConfigStats) {
- auto& configStats = pair.second;
- addSubStatsToConfigLocked(pair.first, configStats);
-
- const int numBytes = configStats.ByteSize();
- vector<char> buffer(numBytes);
- configStats.SerializeToArray(&buffer[0], numBytes);
- proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
- buffer.size());
- // reset the sub stats, the source of truth is in the individual map
- // they will be repopulated when dumpStats() is called again.
- configStats.clear_matcher_stats();
- configStats.clear_condition_stats();
- configStats.clear_metric_stats();
- configStats.clear_alert_stats();
+ addConfigStatsToProto(*(pair.second), &proto);
}
const size_t atomCounts = mPushedAtomStats.size();
@@ -551,10 +565,14 @@
proto.end(token);
}
- const int numBytes = mUidMapStats.ByteSize();
- vector<char> buffer(numBytes);
- mUidMapStats.SerializeToArray(&buffer[0], numBytes);
- proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size());
+ long long uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_SNAPSHOTS, mUidMapStats.snapshots);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS,
+ mUidMapStats.dropped_snapshots);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes);
+ proto.end(uidMapToken);
for (const auto& error : mLoggerErrors) {
long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS |
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 24ac688..c3f4013 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -30,8 +30,49 @@
namespace os {
namespace statsd {
+struct ConfigStats {
+ int32_t uid;
+ int64_t id;
+ int32_t creation_time_sec;
+ int32_t deletion_time_sec = 0;
+ int32_t metric_count;
+ int32_t condition_count;
+ int32_t matcher_count;
+ int32_t alert_count;
+ bool is_valid;
+
+ std::list<int32_t> broadcast_sent_time_sec;
+ std::list<int32_t> data_drop_time_sec;
+ std::list<int32_t> dump_report_time_sec;
+
+ // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
+ std::map<const int64_t, int> matcher_stats;
+
+ // Stores the number of output tuple of condition trackers when it's bigger than
+ // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+ // it means some data has been dropped. The map size is capped by kMaxConfigCount.
+ std::map<const int64_t, int> condition_stats;
+
+ // Stores the number of output tuple of metric producers when it's bigger than
+ // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+ // it means some data has been dropped. The map size is capped by kMaxConfigCount.
+ std::map<const int64_t, int> metric_stats;
+
+ // Stores the number of times an anomaly detection alert has been declared.
+ // The map size is capped by kMaxConfigCount.
+ std::map<const int64_t, int> alert_stats;
+};
+
+struct UidMapStats {
+ int32_t snapshots;
+ int32_t changes;
+ int32_t bytes_used;
+ int32_t dropped_snapshots;
+ int32_t dropped_changes;
+};
+
// Keeps track of stats of statsd.
-// Single instance shared across the process. All methods are thread safe.
+// Single instance shared across the process. All public methods are thread safe.
class StatsdStats {
public:
static StatsdStats& getInstance();
@@ -233,25 +274,15 @@
int32_t mStartTimeSec;
// Track the number of dropped entries used by the uid map.
- StatsdStatsReport_UidMapStats mUidMapStats;
+ UidMapStats mUidMapStats;
// The stats about the configs that are still in use.
// The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
+ std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats;
// Stores the stats for the configs that are no longer in use.
// The size of the vector is capped by kMaxIceBoxSize.
- std::list<const StatsdStatsReport_ConfigStats> mIceBox;
-
- // Stores the number of output tuple of condition trackers when it's bigger than
- // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
- // it means some data has been dropped. The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, std::map<const int64_t, int>> mConditionStats;
-
- // Stores the number of output tuple of metric producers when it's bigger than
- // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
- // it means some data has been dropped. The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, std::map<const int64_t, int>> mMetricsStats;
+ std::list<const std::shared_ptr<ConfigStats>> mIceBox;
// Stores the number of times a pushed atom is logged.
// The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
@@ -272,27 +303,18 @@
// Stores the number of times statsd registers the periodic alarm changes
int mPeriodicAlarmRegisteredStats = 0;
- // Stores the number of times an anomaly detection alert has been declared
- // (per config, per alert name). The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats;
-
- // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, std::map<const int64_t, int>> mMatcherStats;
void noteConfigRemovedInternalLocked(const ConfigKey& key);
void resetInternalLocked();
- void addSubStatsToConfigLocked(const ConfigKey& key,
- StatsdStatsReport_ConfigStats& configStats);
-
void noteDataDropped(const ConfigKey& key, int32_t timeSec);
void noteMetricsReportSent(const ConfigKey& key, int32_t timeSec);
void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
- void addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats);
+ void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index d282b86..4e4f146 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,8 +17,6 @@
#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
-#include <sstream>
-
#include "stats_log_util.h"
namespace android {
@@ -27,7 +25,6 @@
using namespace android::util;
using android::util::ProtoOutputStream;
-using std::ostringstream;
using std::string;
using std::vector;
@@ -381,16 +378,15 @@
}
string LogEvent::ToString() const {
- ostringstream result;
- result << "{ " << mLogdTimestampNs << " " << mElapsedTimestampNs << " (" << mTagId << ")";
+ string result;
+ result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+ (long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
- result << StringPrintf("%#x", value.mField.getField());
- result << "->";
- result << value.mValue.toString();
- result << " ";
+ result +=
+ StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " ";
}
- result << " }";
- return result.str();
+ result += " }";
+ return result;
}
void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 5d5e64e..364d4e9 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -21,7 +21,6 @@
#include "matchers/matcher_util.h"
#include "stats_util.h"
-using std::ostringstream;
using std::set;
using std::string;
using std::unordered_map;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 9912afa..b5afef2 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -21,6 +21,7 @@
#include "../condition/CombinationConditionTracker.h"
#include "../condition/SimpleConditionTracker.h"
+#include "../condition/StateTracker.h"
#include "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
@@ -31,6 +32,7 @@
#include "../metrics/ValueMetricProducer.h"
#include "stats_util.h"
+#include "statslog.h"
using std::set;
using std::string;
@@ -157,6 +159,49 @@
return true;
}
+/**
+ * A StateTracker is built from a SimplePredicate which has only "start", and no "stop"
+ * or "stop_all". The start must be an atom matcher that matches a state atom. It must
+ * have dimension, the dimension must be the state atom's primary fields plus exclusive state
+ * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState.
+ *
+ */
+bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
+ // 1. must not have "stop". must have "dimension"
+ if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
+ // TODO: need to check the start atom matcher too.
+ auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field());
+ // 2. must be based on a state atom.
+ if (it != android::util::kStateAtomsFieldOptions.end()) {
+ // 3. dimension must be primary fields + state field IN ORDER
+ size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
+ vector<Matcher> dimensions;
+ translateFieldMatcher(simplePredicate.dimensions(), &dimensions);
+ if (dimensions.size() != expectedDimensionCount) {
+ return false;
+ }
+ // 3.1 check the primary fields first.
+ size_t index = 0;
+ for (const auto& field : it->second.primaryFields) {
+ Matcher matcher = getSimpleMatcher(it->first, field);
+ if (!(matcher == dimensions[index])) {
+ return false;
+ }
+ primaryKeys->push_back(matcher);
+ index++;
+ }
+ Matcher stateFieldMatcher =
+ getSimpleMatcher(it->first, it->second.exclusiveField);
+ // 3.2 last dimension should be the exclusive field.
+ if (!(dimensions.back() == stateFieldMatcher)) {
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+} // namespace statsd
+
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
const unordered_map<int64_t, int>& logTrackerMap,
unordered_map<int64_t, int>& conditionTrackerMap,
@@ -172,8 +217,16 @@
int index = allConditionTrackers.size();
switch (condition.contents_case()) {
case Predicate::ContentsCase::kSimplePredicate: {
- allConditionTrackers.push_back(new SimpleConditionTracker(
- key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
+ vector<Matcher> primaryKeys;
+ if (isStateTracker(condition.simple_predicate(), &primaryKeys)) {
+ allConditionTrackers.push_back(new StateTracker(key, condition.id(), index,
+ condition.simple_predicate(),
+ logTrackerMap, primaryKeys));
+ } else {
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ key, condition.id(), index, condition.simple_predicate(),
+ logTrackerMap));
+ }
break;
}
case Predicate::ContentsCase::kCombination: {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index edda53d..386de0b 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -110,6 +110,8 @@
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
std::set<int64_t> &noReportMetricIds);
+bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 269f25b..3c5f5a2 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -165,3 +165,86 @@
repeated ConfigMetricsReport reports = 2;
}
+
+message StatsdStatsReport {
+ optional int32 stats_begin_time_sec = 1;
+
+ optional int32 stats_end_time_sec = 2;
+
+ message MatcherStats {
+ optional int64 id = 1;
+ optional int32 matched_times = 2;
+ }
+
+ message ConditionStats {
+ optional int64 id = 1;
+ optional int32 max_tuple_counts = 2;
+ }
+
+ message MetricStats {
+ optional int64 id = 1;
+ optional int32 max_tuple_counts = 2;
+ }
+
+ message AlertStats {
+ optional int64 id = 1;
+ optional int32 alerted_times = 2;
+ }
+
+ message ConfigStats {
+ optional int32 uid = 1;
+ optional int64 id = 2;
+ optional int32 creation_time_sec = 3;
+ optional int32 deletion_time_sec = 4;
+ optional int32 metric_count = 5;
+ optional int32 condition_count = 6;
+ optional int32 matcher_count = 7;
+ optional int32 alert_count = 8;
+ optional bool is_valid = 9;
+
+ repeated int32 broadcast_sent_time_sec = 10;
+ repeated int32 data_drop_time_sec = 11;
+ repeated int32 dump_report_time_sec = 12;
+ repeated MatcherStats matcher_stats = 13;
+ repeated ConditionStats condition_stats = 14;
+ repeated MetricStats metric_stats = 15;
+ repeated AlertStats alert_stats = 16;
+ }
+
+ repeated ConfigStats config_stats = 3;
+
+ message AtomStats {
+ optional int32 tag = 1;
+ optional int32 count = 2;
+ }
+
+ repeated AtomStats atom_stats = 7;
+
+ message UidMapStats {
+ optional int32 snapshots = 1;
+ optional int32 changes = 2;
+ optional int32 bytes_used = 3;
+ optional int32 dropped_snapshots = 4;
+ optional int32 dropped_changes = 5;
+ }
+ optional UidMapStats uidmap_stats = 8;
+
+ message AnomalyAlarmStats {
+ optional int32 alarms_registered = 1;
+ }
+ optional AnomalyAlarmStats anomaly_alarm_stats = 9;
+
+ message PulledAtomStats {
+ optional int32 atom_id = 1;
+ optional int64 total_pull = 2;
+ optional int64 total_pull_from_cache = 3;
+ optional int64 min_pull_interval_sec = 4;
+ }
+ repeated PulledAtomStats pulled_atom_stats = 10;
+
+ message LoggerErrorStats {
+ optional int32 logger_disconnection_sec = 1;
+ optional int32 error_code = 2;
+ }
+ repeated LoggerErrorStats logger_error_stats = 11;
+}
diff --git a/cmds/statsd/src/stats_log_common.proto b/cmds/statsd/src/stats_log_common.proto
index aeecd23..c41f31e 100644
--- a/cmds/statsd/src/stats_log_common.proto
+++ b/cmds/statsd/src/stats_log_common.proto
@@ -46,87 +46,4 @@
optional int64 version = 5;
}
repeated Change changes = 2;
-}
-
-message StatsdStatsReport {
- optional int32 stats_begin_time_sec = 1;
-
- optional int32 stats_end_time_sec = 2;
-
- message MatcherStats {
- optional int64 id = 1;
- optional int32 matched_times = 2;
- }
-
- message ConditionStats {
- optional int64 id = 1;
- optional int32 max_tuple_counts = 2;
- }
-
- message MetricStats {
- optional int64 id = 1;
- optional int32 max_tuple_counts = 2;
- }
-
- message AlertStats {
- optional int64 id = 1;
- optional int32 alerted_times = 2;
- }
-
- message ConfigStats {
- optional int32 uid = 1;
- optional int64 id = 2;
- optional int32 creation_time_sec = 3;
- optional int32 deletion_time_sec = 4;
- optional int32 metric_count = 5;
- optional int32 condition_count = 6;
- optional int32 matcher_count = 7;
- optional int32 alert_count = 8;
- optional bool is_valid = 9;
-
- repeated int32 broadcast_sent_time_sec = 10;
- repeated int32 data_drop_time_sec = 11;
- repeated int32 dump_report_time_sec = 12;
- repeated MatcherStats matcher_stats = 13;
- repeated ConditionStats condition_stats = 14;
- repeated MetricStats metric_stats = 15;
- repeated AlertStats alert_stats = 16;
- }
-
- repeated ConfigStats config_stats = 3;
-
- message AtomStats {
- optional int32 tag = 1;
- optional int32 count = 2;
- }
-
- repeated AtomStats atom_stats = 7;
-
- message UidMapStats {
- optional int32 snapshots = 1;
- optional int32 changes = 2;
- optional int32 bytes_used = 3;
- optional int32 dropped_snapshots = 4;
- optional int32 dropped_changes = 5;
- }
- optional UidMapStats uidmap_stats = 8;
-
- message AnomalyAlarmStats {
- optional int32 alarms_registered = 1;
- }
- optional AnomalyAlarmStats anomaly_alarm_stats = 9;
-
- message PulledAtomStats {
- optional int32 atom_id = 1;
- optional int64 total_pull = 2;
- optional int64 total_pull_from_cache = 3;
- optional int64 min_pull_interval_sec = 4;
- }
- repeated PulledAtomStats pulled_atom_stats = 10;
-
- message LoggerErrorStats {
- optional int32 logger_disconnection_sec = 1;
- optional int32 error_code = 2;
- }
- repeated LoggerErrorStats logger_error_stats = 11;
}
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index c4b47dc..bbaf50a 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -16,7 +16,6 @@
#pragma once
-#include <sstream>
#include "HashableDimensionKey.h"
#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
#include "logd/LogReader.h"
diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateTracker_test.cpp
new file mode 100644
index 0000000..9a66254
--- /dev/null
+++ b/cmds/statsd/tests/condition/StateTracker_test.cpp
@@ -0,0 +1,112 @@
+// 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.
+
+#include "src/condition/StateTracker.h"
+#include "tests/statsd_test_util.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <numeric>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+namespace android {
+namespace os {
+namespace statsd {
+
+const int kUidProcTag = 27;
+
+SimplePredicate getUidProcStatePredicate() {
+ SimplePredicate simplePredicate;
+ simplePredicate.set_start(StringToId("UidProcState"));
+
+ simplePredicate.mutable_dimensions()->set_field(kUidProcTag);
+ simplePredicate.mutable_dimensions()->add_child()->set_field(1);
+ simplePredicate.mutable_dimensions()->add_child()->set_field(2);
+
+ simplePredicate.set_count_nesting(false);
+ return simplePredicate;
+}
+
+void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) {
+ event->write(uid);
+ event->write(state);
+ event->init();
+}
+
+TEST(StateTrackerTest, TestStateChange) {
+ int uid1 = 111;
+ int uid2 = 222;
+
+ int state1 = 1001;
+ int state2 = 1002;
+ unordered_map<int64_t, int> trackerNameIndexMap;
+ trackerNameIndexMap[StringToId("UidProcState")] = 0;
+ vector<Matcher> primaryFields;
+ primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1));
+ StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
+ trackerNameIndexMap, primaryFields);
+
+ LogEvent event(kUidProcTag, 0 /*timestamp*/);
+ makeUidProcStateEvent(uid1, state1, &event);
+
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ vector<sp<ConditionTracker>> allPredicates;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
+ EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+ EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ changedCache[0] = false;
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
+ EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size());
+ EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+ EXPECT_FALSE(changedCache[0]);
+
+ LogEvent event2(kUidProcTag, 0 /*timestamp*/);
+ makeUidProcStateEvent(uid1, state2, &event2);
+
+ changedCache[0] = false;
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache);
+ EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+ EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ LogEvent event3(kUidProcTag, 0 /*timestamp*/);
+ makeUidProcStateEvent(uid2, state1, &event3);
+ changedCache[0] = false;
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache);
+ EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+ EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+ EXPECT_TRUE(changedCache[0]);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index bd11443..5c4eda8 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -277,21 +277,20 @@
EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
const auto& configStats = stats.mConfigStats[key];
- int maxCount = StatsdStats::kMaxTimestampCount;
- EXPECT_EQ(maxCount, configStats.broadcast_sent_time_sec_size());
- EXPECT_EQ(maxCount, configStats.data_drop_time_sec_size());
- EXPECT_EQ(maxCount, configStats.dump_report_time_sec_size());
+ size_t maxCount = StatsdStats::kMaxTimestampCount;
+ EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
+ EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
+ EXPECT_EQ(maxCount, configStats->dump_report_time_sec.size());
// the oldest timestamp is the second timestamp in history
- EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0));
- EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0));
- EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0));
+ EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
+ EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
+ EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
// the last timestamp is the newest timestamp.
- EXPECT_EQ(newTimestamp,
- configStats.broadcast_sent_time_sec(StatsdStats::kMaxTimestampCount - 1));
- EXPECT_EQ(newTimestamp, configStats.data_drop_time_sec(StatsdStats::kMaxTimestampCount - 1));
- EXPECT_EQ(newTimestamp, configStats.dump_report_time_sec(StatsdStats::kMaxTimestampCount - 1));
+ EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
+ EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
+ EXPECT_EQ(newTimestamp, configStats->dump_report_time_sec.back());
}
} // namespace statsd
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index a12def0..bed4d98 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -50,7 +50,7 @@
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLogCommon.StatsdStatsReport;
+import com.android.os.StatsLog.StatsdStatsReport;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
import java.util.ArrayList;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
index 58cbcd8..3939e7e 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
@@ -16,7 +16,7 @@
package com.android.statsd.loadtest;
import android.content.Context;
-import com.android.os.StatsLogCommon.StatsdStatsReport;
+import com.android.os.StatsLog.StatsdStatsReport;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
public class StatsdStatsRecorder extends PerfDataRecorder {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 752b662..2c400e0 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -659,7 +659,6 @@
Landroid/graphics/drawable/GradientDrawable$GradientState;->mPadding:Landroid/graphics/Rect;
Landroid/graphics/drawable/GradientDrawable$GradientState;->mPositions:[F
Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/Icon;->getResPackage()Ljava/lang/String;
Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
@@ -977,8 +976,6 @@
Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
-Landroid/media/Ringtone;->setLooping(Z)V
-Landroid/media/Ringtone;->setVolume(F)V
Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
@@ -2306,6 +2303,12 @@
Lcom/android/internal/R$integer;->config_screenBrightnessDim:I
Lcom/android/internal/R$integer;->config_toastDefaultGravity:I
Lcom/android/internal/R$layout;->screen_title:I
+Lcom/android/internal/R$string;->byteShort:I
+Lcom/android/internal/R$string;->gigabyteShort:I
+Lcom/android/internal/R$string;->kilobyteShort:I
+Lcom/android/internal/R$string;->megabyteShort:I
+Lcom/android/internal/R$string;->petabyteShort:I
+Lcom/android/internal/R$string;->terabyteShort:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 03faeee..2d73ce0 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -44,7 +44,6 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
-import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -71,6 +70,7 @@
import com.android.internal.os.RoSystemProperties;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.MemInfoReader;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlSerializer;
@@ -964,6 +964,17 @@
}
/**
+ * Return the total number of bytes of RAM this device has.
+ * @hide
+ */
+ @TestApi
+ public long getTotalRam() {
+ MemInfoReader memreader = new MemInfoReader();
+ memreader.readMemInfo();
+ return memreader.getTotalSize();
+ }
+
+ /**
* Return the maximum number of recents entries that we will maintain and show.
* @hide
*/
@@ -2751,30 +2762,6 @@
}
/**
- * Updates (grants or revokes) a persitable URI permission.
- *
- * @param uri URI to be granted or revoked.
- * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
- * applies to all URIs that are prefixed by this URI.
- * @param packageName target package.
- * @param grant if {@code true} a new permission will be granted, otherwise an existing
- * permission will be revoked.
- *
- * @return whether or not the requested succeeded.
- *
- * @hide
- */
- public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
- boolean grant) {
- try {
- return getService().updatePersistableUriPermission(uri, prefix, packageName, grant,
- UserHandle.myUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Information you can retrieve about any processes that are in an error condition.
*/
public static class ProcessErrorStateInfo implements Parcelable {
@@ -3335,6 +3322,28 @@
}
/**
+ * Query whether the user has enabled background restrictions for this app.
+ *
+ * <p> The user may chose to do this, if they see that an app is consuming an unreasonable
+ * amount of battery while in the background. </p>
+ *
+ * <p> If true, any work that the app tries to do will be aggressively restricted while it is in
+ * the background. At a minimum, jobs and alarms will not execute and foreground services
+ * cannot be started unless an app activity is in the foreground. </p>
+ *
+ * <p><b> Note that these restrictions stay in effect even when the device is charging.</b></p>
+ *
+ * @return true if user has enforced background restrictions for this app, false otherwise.
+ */
+ public boolean isBackgroundRestricted() {
+ try {
+ return getService().isBackgroundRestricted(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the memory trim mode for a process and schedules a memory trim operation.
*
* <p><b>Note: this method is only intended for testing framework.</b></p>
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 872370e..379944e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -143,7 +143,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.proto.MemInfoDumpProto;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.CloseGuard;
@@ -1251,55 +1251,62 @@
long parcelCount = Parcel.getGlobalAllocCount();
SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
- final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
- proto.write(MemInfoProto.ProcessMemory.PID, Process.myPid());
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME,
+ final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid());
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME,
(mBoundApplication != null) ? mBoundApplication.processName : "unknown");
dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly,
nativeMax, nativeAllocated, nativeFree,
dalvikMax, dalvikAllocated, dalvikFree);
proto.end(mToken);
- final long oToken = proto.start(MemInfoProto.AppData.OBJECTS);
- proto.write(MemInfoProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, viewInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
+ final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT,
+ viewInstanceCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
viewRootInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
appContextInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
activityInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, globalAssetCount);
- proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT,
+ globalAssetCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
globalAssetManagerCount);
- proto.write(MemInfoProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
binderLocalObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
binderProxyObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_MEMORY_KB, parcelSize / 1024);
- proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
- proto.write(MemInfoProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB,
+ parcelSize / 1024);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
binderDeathObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, openSslSocketCount);
- proto.write(MemInfoProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT,
+ openSslSocketCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
webviewInstanceCount);
proto.end(oToken);
// SQLite mem info
- final long sToken = proto.start(MemInfoProto.AppData.SQL);
- proto.write(MemInfoProto.AppData.SqlStats.MEMORY_USED_KB, stats.memoryUsed / 1024);
- proto.write(MemInfoProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
+ final long sToken = proto.start(MemInfoDumpProto.AppData.SQL);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB,
+ stats.memoryUsed / 1024);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
stats.pageCacheOverflow / 1024);
- proto.write(MemInfoProto.AppData.SqlStats.MALLOC_SIZE_KB, stats.largestMemAlloc / 1024);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB,
+ stats.largestMemAlloc / 1024);
int n = stats.dbStats.size();
for (int i = 0; i < n; i++) {
DbStats dbStats = stats.dbStats.get(i);
- final long dToken = proto.start(MemInfoProto.AppData.SqlStats.DATABASES);
- proto.write(MemInfoProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
- proto.write(MemInfoProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
- proto.write(MemInfoProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
- proto.write(MemInfoProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside);
- proto.write(MemInfoProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
+ final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B,
+ dbStats.lookaside);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
proto.end(dToken);
}
proto.end(sToken);
@@ -1307,7 +1314,7 @@
// Asset details.
String assetAlloc = AssetManager.getAssetAllocations();
if (assetAlloc != null) {
- proto.write(MemInfoProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
+ proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
}
// Unreachable native memory
@@ -1315,7 +1322,7 @@
int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags;
boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
|| android.os.Build.IS_DEBUGGABLE;
- proto.write(MemInfoProto.AppData.UNREACHABLE_MEMORY,
+ proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY,
Debug.getUnreachableMemory(100, showContents));
}
}
@@ -2505,17 +2512,17 @@
boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) {
final long token = proto.start(fieldId);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.NAME, name);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.NAME, name);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
if (hasSwappedOutPss) {
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
} else {
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
}
proto.end(token);
@@ -2530,26 +2537,26 @@
long dalvikMax, long dalvikAllocated, long dalvikFree) {
if (!dumpSummaryOnly) {
- final long nhToken = proto.start(MemInfoProto.ProcessMemory.NATIVE_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
+ final long nhToken = proto.start(MemInfoDumpProto.ProcessMemory.NATIVE_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
memInfo.nativePrivateClean, memInfo.hasSwappedOutPss,
memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
proto.end(nhToken);
- final long dvToken = proto.start(MemInfoProto.ProcessMemory.DALVIK_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
+ final long dvToken = proto.start(MemInfoDumpProto.ProcessMemory.DALVIK_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss,
memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
proto.end(dvToken);
int otherPss = memInfo.otherPss;
@@ -2573,7 +2580,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.OTHER_HEAPS,
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.OTHER_HEAPS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2590,21 +2597,23 @@
}
}
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
otherPss, otherSwappablePss,
otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss);
- final long tToken = proto.start(MemInfoProto.ProcessMemory.TOTAL_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
+ final long tToken = proto.start(MemInfoDumpProto.ProcessMemory.TOTAL_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
memInfo.getTotalPss(), memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(),
memInfo.getTotalSwappedOutPss());
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB,
+ nativeMax + dalvikMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
nativeAllocated + dalvikAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB,
+ nativeFree + dalvikFree);
proto.end(tToken);
if (dumpDalvik) {
@@ -2622,7 +2631,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.DALVIK_DETAILS,
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.DALVIK_DETAILS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2632,24 +2641,26 @@
}
}
- final long asToken = proto.start(MemInfoProto.ProcessMemory.APP_SUMMARY);
- proto.write(MemInfoProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
+ final long asToken = proto.start(MemInfoDumpProto.ProcessMemory.APP_SUMMARY);
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
memInfo.getSummaryJavaHeap());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
memInfo.getSummaryNativeHeap());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_PSS_KB,
+ memInfo.getSummaryCode());
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_PSS_KB,
+ memInfo.getSummaryStack());
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
memInfo.getSummaryGraphics());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
memInfo.getSummaryPrivateOther());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
memInfo.getSummarySystem());
if (memInfo.hasSwappedOutPss) {
- proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwapPss());
} else {
- proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwap());
}
proto.end(asToken);
@@ -3716,6 +3727,10 @@
if (localLOGV) Slog.v(TAG, "Performing resume of " + r
+ " finished=" + r.activity.mFinished);
if (r != null && !r.activity.mFinished) {
+ if (r.getLifecycleState() == ON_RESUME) {
+ throw new IllegalStateException(
+ "Trying to resume activity which is already resumed");
+ }
if (clearHide) {
r.hideForNow = false;
r.activity.mStartedActivity = false;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c5b3a4a..05a9861 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -31,7 +31,6 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -168,12 +167,14 @@
/** @hide */
public static final int OP_WRITE_SETTINGS = 23;
/** @hide Required to draw on top of other apps. */
+ @TestApi
public static final int OP_SYSTEM_ALERT_WINDOW = 24;
/** @hide */
public static final int OP_ACCESS_NOTIFICATIONS = 25;
/** @hide */
public static final int OP_CAMERA = 26;
/** @hide */
+ @TestApi
public static final int OP_RECORD_AUDIO = 27;
/** @hide */
public static final int OP_PLAY_AUDIO = 28;
@@ -1540,6 +1541,7 @@
*
* @hide
*/
+ @TestApi
public interface OnOpActiveChangedListener {
/**
* Called when the active state of an app op changes.
@@ -1731,15 +1733,14 @@
* Monitor for changes to the operating mode for the given op in the given app package.
*
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
- * to watch changes only for your UID.
+ * you can watch changes only for your UID.
*
* @param op The operation to monitor, one of OP_*.
* @param packageName The name of the application to monitor.
* @param callback Where to report changes.
* @hide
*/
- // TODO: Uncomment below annotation once b/73559440 is fixed
- // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) {
synchronized (mModeWatchers) {
IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -1788,6 +1789,9 @@
* watched ops for a registered callback you need to unregister and register it
* again.
*
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can watch changes only for your UID.
+ *
* @param ops The ops to watch.
* @param callback Where to report changes.
*
@@ -1798,7 +1802,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+ @TestApi
+ // TODO: Uncomment below annotation once b/73559440 is fixed
+ // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
public void startWatchingActive(@NonNull int[] ops,
@NonNull OnOpActiveChangedListener callback) {
Preconditions.checkNotNull(ops, "ops cannot be null");
@@ -1836,6 +1842,7 @@
*
* @hide
*/
+ @TestApi
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
synchronized (mActiveWatchers) {
final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
@@ -2087,15 +2094,11 @@
* @hide
*/
public int noteOp(int op, int uid, String packageName) {
- try {
- int mode = mService.noteOperation(op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ final int mode = noteOpNoThrow(op, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
+ return mode;
}
/**
@@ -2174,6 +2177,11 @@
}
}
+ /** @hide */
+ public int startOp(int op) {
+ return startOp(op, Process.myUid(), mContext.getOpPackageName());
+ }
+
/**
* Report that an application has started executing a long-running operation. Note that you
* must pass in both the uid and name of the application to be checked; this function will
@@ -2182,6 +2190,7 @@
* the current time and the operation will be marked as "running". In this case you must
* later call {@link #finishOp(int, int, String)} to report when the application is no
* longer performing the operation.
+ *
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -2192,15 +2201,34 @@
* @hide
*/
public int startOp(int op, int uid, String packageName) {
- try {
- int mode = mService.startOperation(getToken(mService), op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return startOp(op, uid, packageName, false);
+ }
+
+ /**
+ * Report that an application has started executing a long-running operation. Similar
+ * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT}
+ * the operation should succeed since the caller has performed its standard permission
+ * checks which passed and would perform the protected operation for this mode.
+ *
+ * @param op The operation to start. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+ *
+ * @throws SecurityException If the app has been configured to crash on this op or
+ * the package is not in the passed in UID.
+ *
+ * @hide
+ */
+ public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+ final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
+ return mode;
}
/**
@@ -2209,18 +2237,32 @@
* @hide
*/
public int startOpNoThrow(int op, int uid, String packageName) {
+ return startOpNoThrow(op, uid, packageName, false);
+ }
+
+ /**
+ * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a
+ * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ *
+ * @param op The operation to start. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+ *
+ * @hide
+ */
+ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
try {
- return mService.startOperation(getToken(mService), op, uid, packageName);
+ return mService.startOperation(getToken(mService), op, uid, packageName,
+ startIfModeDefault);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /** @hide */
- public int startOp(int op) {
- return startOp(op, Process.myUid(), mContext.getOpPackageName());
- }
-
/**
* Report that an application is no longer performing an operation that had previously
* been started with {@link #startOp(int, int, String)}. There is no validation of input
@@ -2241,8 +2283,21 @@
finishOp(op, Process.myUid(), mContext.getOpPackageName());
}
- /** @hide */
- @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+ /**
+ * Checks whether the given op for a UID and package is active.
+ *
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can query only for your UID.
+ *
+ * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+ * @see #stopWatchingMode(OnOpChangedListener)
+ * @see #finishOp(int)
+ * @see #startOp(int)
+ *
+ * @hide */
+ @TestApi
+ // TODO: Uncomment below annotation once b/73559440 is fixed
+ // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
public boolean isOperationActive(int code, int uid, String packageName) {
try {
return mService.isOperationActive(code, uid, packageName);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index ac301b3..0d45dfa 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -423,10 +423,8 @@
void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
void restart();
void performIdleMaintenance();
- void takePersistableUriPermission(in Uri uri, int modeFlags, int userId);
- boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName,
- boolean grant, int userId);
- void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId);
+ void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
+ void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
void appNotRespondingViaProvider(in IBinder connection);
Rect getTaskBounds(int taskId);
@@ -614,7 +612,7 @@
int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
in String requiredPermission, in Bundle options);
-
+ boolean isBackgroundRestricted(in String packageName);
// Start of N MR1 transactions
void setVrThread(int tid);
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 2ba4c00..58d0aaf 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -62,7 +62,7 @@
@Deprecated
public void onGetInstantAppResolveInfo(
int digestPrefix[], String token, InstantAppResolutionCallback callback) {
- throw new IllegalStateException("Must define");
+ throw new IllegalStateException("Must define onGetInstantAppResolveInfo");
}
/**
@@ -80,10 +80,26 @@
}
/**
- * Called to retrieve resolve info for instant applications immediately.
+ * Called to retrieve resolve info for instant applications immediately. The response will be
+ * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
+ * in response to this method may be partial to request a second phase of resolution which will
+ * result in a subsequent call to
+ * {@link #onGetInstantAppIntentFilter(Intent, int[], String, InstantAppResolutionCallback)}
*
- * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
+ *
+ * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent
+ * is an intent with potential PII removed from the original intent.
+ * Fields removed include extras and the host + path of the data, if
+ * defined.
* @param hostDigestPrefix The hash prefix of the instant app's domain.
+ * @param token A unique identifier that will be provided in calls to
+ * {@link #onGetInstantAppIntentFilter(Intent, int[], String,
+ * InstantAppResolutionCallback)}
+ * and provided to the installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN} to
+ * tie a single launch together.
+ * @param callback The {@link InstantAppResolutionCallback} to provide results to.
+ *
+ * @see InstantAppResolveInfo
*/
public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix,
String token, InstantAppResolutionCallback callback) {
@@ -96,12 +112,20 @@
}
/**
- * Called to retrieve intent filters for instant applications from potentially expensive
- * sources.
+ * Called to retrieve intent filters for potentially matching instant applications. Unlike
+ * {@link #onGetInstantAppResolveInfo(Intent, int[], String, InstantAppResolutionCallback)},
+ * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s
+ * provided in response to this method must be completely populated.
*
* @param sanitizedIntent The sanitized {@link Intent} used for resolution.
* @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
* defined.
+ * @param token A unique identifier that was provided in
+ * {@link #onGetInstantAppResolveInfo(Intent, int[], String,
+ * InstantAppResolutionCallback)}
+ * and provided to the currently visible installer via
+ * {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
+ * @param callback The {@link InstantAppResolutionCallback} to provide results to
*/
public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix,
String token, InstantAppResolutionCallback callback) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 233e09d..13a6be5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -360,6 +360,23 @@
@Deprecated
public RemoteViews headsUpContentView;
+ private boolean mUsesStandardHeader;
+
+ private static final ArraySet<Integer> STANDARD_LAYOUTS = new ArraySet<>();
+ static {
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_base);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_header);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
+ }
+
/**
* A large bitmap to be shown in the notification content area.
*
@@ -2534,6 +2551,8 @@
}
parcel.writeInt(mGroupAlertBehavior);
+
+ // mUsesStandardHeader is not written because it should be recomputed in listeners
}
/**
@@ -4092,6 +4111,25 @@
}
}
+ /**
+ * @hide
+ */
+ public boolean usesStandardHeader() {
+ if (mN.mUsesStandardHeader) {
+ return true;
+ }
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
+ if (mN.contentView == null && mN.bigContentView == null) {
+ return true;
+ }
+ }
+ boolean contentViewUsesHeader = mN.contentView == null
+ || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId());
+ boolean bigContentViewUsesHeader = mN.bigContentView == null
+ || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId());
+ return contentViewUsesHeader && bigContentViewUsesHeader;
+ }
+
private void resetStandardTemplate(RemoteViews contentView) {
resetNotificationHeader(contentView);
resetContentMargins(contentView);
@@ -4123,6 +4161,7 @@
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
+ mN.mUsesStandardHeader = false;
}
private void resetContentMargins(RemoteViews contentView) {
@@ -4444,6 +4483,7 @@
bindProfileBadge(contentView);
}
bindExpandButton(contentView);
+ mN.mUsesStandardHeader = true;
}
private void bindExpandButton(RemoteViews contentView) {
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 4e2cb64..49faf40 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -33,7 +33,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -752,6 +751,8 @@
*
* <p>This function can be safely called at any time (even if no search is active.)
*
+ * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+ *
* @see #startSearch
*/
public void stopSearch() {
@@ -802,6 +803,8 @@
/**
* Set or clear the callback that will be invoked whenever the search UI is dismissed.
*
+ * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+ *
* @param listener The {@link OnDismissListener} to use, or null.
*/
public void setOnDismissListener(final OnDismissListener listener) {
@@ -811,6 +814,8 @@
/**
* Set or clear the callback that will be invoked whenever the search UI is canceled.
*
+ * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+ *
* @param listener The {@link OnCancelListener} to use, or null.
*/
public void setOnCancelListener(OnCancelListener listener) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2c4bf82..ee5ea80 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3712,7 +3712,9 @@
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
/**
- * Disable all keyguard widgets. Has no effect.
+ * Disable all keyguard widgets. Has no effect starting from
+ * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported
+ * on Android versions lower than 5.0.
*/
public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1312a2e..ef41b10 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1309,6 +1309,11 @@
if (domain == null) return;
final Uri uri = Uri.parse(domain);
+ if (uri == null) {
+ // Cannot log domain because it could contain PII;
+ Log.w(TAG, "Failed to parse web domain");
+ return;
+ }
mWebScheme = uri.getScheme();
mWebDomain = uri.getHost();
}
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 73b5ec4..545463c 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,7 +16,7 @@
package android.app.servertransaction;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.app.ClientTransactionHandler;
@@ -38,8 +38,8 @@
private List<ResultInfo> mResultInfoList;
@Override
- public int getPreExecutionState() {
- return ON_PAUSE;
+ public int getPostExecutionState() {
+ return ON_RESUME;
}
@Override
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index 6f2cc00..d94f08b 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -32,12 +32,6 @@
*/
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
- /** Get the state in which this callback can be executed. */
- @LifecycleState
- public int getPreExecutionState() {
- return UNDEFINED;
- }
-
/** Get the state that must follow this callback. */
@LifecycleState
public int getPostExecutionState() {
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 7dfde73..e5ce3b0 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -38,11 +38,6 @@
// TODO(lifecycler): Switch new intent handling to this scheme.
/*@Override
- public int getPreExecutionState() {
- return ON_PAUSE;
- }
-
- @Override
public int getPostExecutionState() {
return ON_RESUME;
}*/
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 059e0af..0e52b34 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -24,6 +24,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -48,11 +49,7 @@
private ClientTransactionHandler mTransactionHandler;
private PendingTransactionActions mPendingActions = new PendingTransactionActions();
-
- // Temp holder for lifecycle path.
- // No direct transition between two states should take more than one complete cycle of 6 states.
- @ActivityLifecycleItem.LifecycleState
- private IntArray mLifecycleSequence = new IntArray(6);
+ private TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
@@ -89,13 +86,25 @@
final IBinder token = transaction.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
+ // In case when post-execution state of the last callback matches the final state requested
+ // for the activity in this transaction, we won't do the last transition here and do it when
+ // moving to final state instead (because it may contain additional parameters from server).
+ final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
+ final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
+ : UNDEFINED;
+ // Index of the last callback that requests some post-execution state.
+ final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
+
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
log("Resolving callback: " + item);
- final int preExecutionState = item.getPreExecutionState();
- if (preExecutionState != UNDEFINED) {
- cycleToPath(r, preExecutionState);
+ final int postExecutionState = item.getPostExecutionState();
+ final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
+ item.getPostExecutionState());
+ if (closestPreExecutionState != UNDEFINED) {
+ cycleToPath(r, closestPreExecutionState);
}
item.execute(mTransactionHandler, token, mPendingActions);
@@ -105,9 +114,11 @@
r = mTransactionHandler.getActivityClient(token);
}
- final int postExecutionState = item.getPostExecutionState();
- if (postExecutionState != UNDEFINED) {
- cycleToPath(r, postExecutionState);
+ if (postExecutionState != UNDEFINED && r != null) {
+ // Skip the very last transition and perform it by explicit state request instead.
+ final boolean shouldExcludeLastTransition =
+ i == lastCallbackRequestingState && finalState == postExecutionState;
+ cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
}
}
}
@@ -162,15 +173,15 @@
boolean excludeLastState) {
final int start = r.getLifecycleState();
log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
- initLifecyclePath(start, finish, excludeLastState);
- performLifecycleSequence(r);
+ final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
+ performLifecycleSequence(r, path);
}
/** Transition the client through previously initialized state sequence. */
- private void performLifecycleSequence(ActivityClientRecord r) {
- final int size = mLifecycleSequence.size();
+ private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
+ final int size = path.size();
for (int i = 0, state; i < size; i++) {
- state = mLifecycleSequence.get(i);
+ state = path.get(i);
log("Transitioning to state: " + state);
switch (state) {
case ON_CREATE:
@@ -195,8 +206,7 @@
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
- "performLifecycleSequence. cycling to:"
- + mLifecycleSequence.get(size - 1));
+ "performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
@@ -207,60 +217,6 @@
}
}
- /**
- * Calculate the path through main lifecycle states for an activity and fill
- * @link #mLifecycleSequence} with values starting with the state that follows the initial
- * state.
- */
- public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
- mLifecycleSequence.clear();
- if (finish >= start) {
- // just go there
- for (int i = start + 1; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- } else { // finish < start, can't just cycle down
- if (start == ON_PAUSE && finish == ON_RESUME) {
- // Special case when we can just directly go to resumed state.
- mLifecycleSequence.add(ON_RESUME);
- } else if (start <= ON_STOP && finish >= ON_START) {
- // Restart and go to required state.
-
- // Go to stopped state first.
- for (int i = start + 1; i <= ON_STOP; i++) {
- mLifecycleSequence.add(i);
- }
- // Restart
- mLifecycleSequence.add(ON_RESTART);
- // Go to required state
- for (int i = ON_START; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- } else {
- // Relaunch and go to required state
-
- // Go to destroyed state first.
- for (int i = start + 1; i <= ON_DESTROY; i++) {
- mLifecycleSequence.add(i);
- }
- // Go to required state
- for (int i = ON_CREATE; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- }
- }
-
- // Remove last transition in case we want to perform it with some specific params.
- if (excludeLastState && mLifecycleSequence.size() != 0) {
- mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
- }
- }
-
- @VisibleForTesting
- public int[] getLifecycleSequence() {
- return mLifecycleSequence.toArray();
- }
-
private static void log(String message) {
if (DEBUG_RESOLVER) Slog.d(TAG, message);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
new file mode 100644
index 0000000..7e66fd7
--- /dev/null
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.app.ActivityThread;
+import android.util.IntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution.
+ * @hide
+ */
+public class TransactionExecutorHelper {
+ // A penalty applied to path with destruction when looking for the shortest one.
+ private static final int DESTRUCTION_PENALTY = 10;
+
+ private static final int[] ON_RESUME_PRE_EXCUTION_STATES = new int[] { ON_START, ON_PAUSE };
+
+ // Temp holder for lifecycle path.
+ // No direct transition between two states should take more than one complete cycle of 6 states.
+ @ActivityLifecycleItem.LifecycleState
+ private IntArray mLifecycleSequence = new IntArray(6);
+
+ /**
+ * Calculate the path through main lifecycle states for an activity and fill
+ * @link #mLifecycleSequence} with values starting with the state that follows the initial
+ * state.
+ * <p>NOTE: The returned value is used internally in this class and is not a copy. It's contents
+ * may change after calling other methods of this class.</p>
+ */
+ @VisibleForTesting
+ public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
+ if (start == UNDEFINED || finish == UNDEFINED) {
+ throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
+ }
+ if (start == ON_RESTART || finish == ON_RESTART) {
+ throw new IllegalArgumentException(
+ "Can't start or finish in intermittent RESTART state");
+ }
+ if (finish == PRE_ON_CREATE && start != finish) {
+ throw new IllegalArgumentException("Can only start in pre-onCreate state");
+ }
+
+ mLifecycleSequence.clear();
+ if (finish >= start) {
+ // just go there
+ for (int i = start + 1; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ } else { // finish < start, can't just cycle down
+ if (start == ON_PAUSE && finish == ON_RESUME) {
+ // Special case when we can just directly go to resumed state.
+ mLifecycleSequence.add(ON_RESUME);
+ } else if (start <= ON_STOP && finish >= ON_START) {
+ // Restart and go to required state.
+
+ // Go to stopped state first.
+ for (int i = start + 1; i <= ON_STOP; i++) {
+ mLifecycleSequence.add(i);
+ }
+ // Restart
+ mLifecycleSequence.add(ON_RESTART);
+ // Go to required state
+ for (int i = ON_START; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ } else {
+ // Relaunch and go to required state
+
+ // Go to destroyed state first.
+ for (int i = start + 1; i <= ON_DESTROY; i++) {
+ mLifecycleSequence.add(i);
+ }
+ // Go to required state
+ for (int i = ON_CREATE; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ }
+ }
+
+ // Remove last transition in case we want to perform it with some specific params.
+ if (excludeLastState && mLifecycleSequence.size() != 0) {
+ mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
+ }
+
+ return mLifecycleSequence;
+ }
+
+ /**
+ * Pick a state that goes before provided post-execution state and would require the least
+ * lifecycle transitions to get to.
+ * It will also make sure to try avoiding a path with activity destruction and relaunch if
+ * possible.
+ * @param r An activity that we're trying to resolve the transition for.
+ * @param postExecutionState Post execution state to compute for.
+ * @return One of states that precede the provided post-execution state, or
+ * {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
+ */
+ @VisibleForTesting
+ public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+ int postExecutionState) {
+ switch (postExecutionState) {
+ case UNDEFINED:
+ return UNDEFINED;
+ case ON_RESUME:
+ return getClosestOfStates(r, ON_RESUME_PRE_EXCUTION_STATES);
+ default:
+ throw new UnsupportedOperationException("Pre-execution states for state: "
+ + postExecutionState + " is not supported.");
+ }
+ }
+
+ /**
+ * Pick a state that would require the least lifecycle transitions to get to.
+ * It will also make sure to try avoiding a path with activity destruction and relaunch if
+ * possible.
+ * @param r An activity that we're trying to resolve the transition for.
+ * @param finalStates An array of valid final states.
+ * @return One of the provided final states, or {@link ActivityLifecycleItem#UNDEFINED} if none
+ * were provided or there is not path.
+ */
+ @VisibleForTesting
+ public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+ if (finalStates == null || finalStates.length == 0) {
+ return UNDEFINED;
+ }
+
+ final int currentState = r.getLifecycleState();
+ int closestState = UNDEFINED;
+ for (int i = 0, shortestPath = Integer.MAX_VALUE, pathLength; i < finalStates.length; i++) {
+ getLifecyclePath(currentState, finalStates[i], false /* excludeLastState */);
+ pathLength = mLifecycleSequence.size();
+ if (pathInvolvesDestruction(mLifecycleSequence)) {
+ pathLength += DESTRUCTION_PENALTY;
+ }
+ if (shortestPath > pathLength) {
+ shortestPath = pathLength;
+ closestState = finalStates[i];
+ }
+ }
+ return closestState;
+ }
+
+ /**
+ * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
+ * that involves destruction and recreation if there is another path.
+ */
+ private static boolean pathInvolvesDestruction(IntArray lifecycleSequence) {
+ final int size = lifecycleSequence.size();
+ for (int i = 0; i < size; i++) {
+ if (lifecycleSequence.get(i) == ON_DESTROY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the index of the last callback that requests the state in which activity will be after
+ * execution. If there is a group of callbacks in the end that requests the same specific state
+ * or doesn't request any - we will find the first one from such group.
+ *
+ * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
+ * specific state. If there is a sequence
+ * Configuration - ActivityResult - Configuration - ActivityResult
+ * index 1 will be returned, because ActivityResult request on position 1 will be the last
+ * request that moves activity to the RESUMED state where it will eventually end.
+ */
+ static int lastCallbackRequestingState(ClientTransaction transaction) {
+ final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
+ if (callbacks == null || callbacks.size() == 0) {
+ return -1;
+ }
+
+ // Go from the back of the list to front, look for the request closes to the beginning that
+ // requests the state in which activity will end after all callbacks are executed.
+ int lastRequestedState = UNDEFINED;
+ int lastRequestingCallback = -1;
+ for (int i = callbacks.size() - 1; i >= 0; i--) {
+ final ClientTransactionItem callback = callbacks.get(i);
+ final int postExecutionState = callback.getPostExecutionState();
+ if (postExecutionState != UNDEFINED) {
+ // Found a callback that requests some post-execution state.
+ if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) {
+ // It's either a first-from-end callback that requests state or it requests
+ // the same state as the last one. In both cases, we will use it as the new
+ // candidate.
+ lastRequestedState = postExecutionState;
+ lastRequestingCallback = i;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return lastRequestingCallback;
+ }
+}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b5c69d8..65e54f9 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -65,7 +65,8 @@
HINT_TOGGLE,
HINT_HORIZONTAL,
HINT_PARTIAL,
- HINT_SEE_MORE
+ HINT_SEE_MORE,
+ HINT_KEY_WORDS
})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceHint {}
@@ -149,6 +150,11 @@
*/
public static final String HINT_CALLER_NEEDED = "caller_needed";
/**
+ * A hint to indicate that the contents of this subslice represent a list of keywords
+ * related to the parent slice.
+ */
+ public static final String HINT_KEY_WORDS = "key_words";
+ /**
* Key to retrieve an extra added to an intent when a control is changed.
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index a2e714e..94fd138 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.media.AudioManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
@@ -599,34 +598,6 @@
}
/**
- * Tells remote device to adjust volume. Only if absolute volume is
- * supported. Uses the following values:
- * <ul>
- * <li>{@link AudioManager#ADJUST_LOWER}</li>
- * <li>{@link AudioManager#ADJUST_RAISE}</li>
- * <li>{@link AudioManager#ADJUST_MUTE}</li>
- * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
- * </ul>
- *
- * @param direction One of the supported adjust values.
- * @hide
- */
- public void adjustAvrcpAbsoluteVolume(int direction) {
- if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
- try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- mService.adjustAvrcpAbsoluteVolume(direction);
- }
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
- }
-
- /**
* Tells remote device to set an absolute volume. Only if absolute volume is supported
*
* @param volume Absolute volume to be set on AVRCP side
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 1a5de56..cb35357 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -84,7 +84,7 @@
public abstract void onDeviceFound(IntentSender chooserLauncher);
/**
- * Called if there was an error looking for device(s), e.g. timeout
+ * Called if there was an error looking for device(s)
*
* @param error the cause of the error
*/
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 22496a4..10331d4 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2102,7 +2102,23 @@
Preconditions.checkNotNull(uri, "uri");
try {
ActivityManager.getService().takePersistableUriPermission(
- ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+ resolveUserId(uri));
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void takePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri,
+ @Intent.AccessUriMode int modeFlags) {
+ Preconditions.checkNotNull(toPackage, "toPackage");
+ Preconditions.checkNotNull(uri, "uri");
+ try {
+ ActivityManager.getService().takePersistableUriPermission(
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
+ resolveUserId(uri));
} catch (RemoteException e) {
}
}
@@ -2120,7 +2136,23 @@
Preconditions.checkNotNull(uri, "uri");
try {
ActivityManager.getService().releasePersistableUriPermission(
- ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+ resolveUserId(uri));
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void releasePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri,
+ @Intent.AccessUriMode int modeFlags) {
+ Preconditions.checkNotNull(toPackage, "toPackage");
+ Preconditions.checkNotNull(uri, "uri");
+ try {
+ ActivityManager.getService().releasePersistableUriPermission(
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
+ resolveUserId(uri));
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index a957aed..cec3bad 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1872,9 +1872,10 @@
du.println(sb.toString());
}
}
- if (mPriority != 0 || mHasPartialTypes) {
+ if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) {
sb.setLength(0);
sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
+ sb.append(", mOrder="); sb.append(mOrder);
sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
du.println(sb.toString());
}
@@ -1951,6 +1952,7 @@
dest.writeInt(mHasPartialTypes ? 1 : 0);
dest.writeInt(getAutoVerify() ? 1 : 0);
dest.writeInt(mInstantAppVisibility);
+ dest.writeInt(mOrder);
}
/**
@@ -2020,6 +2022,7 @@
mHasPartialTypes = source.readInt() > 0;
setAutoVerify(source.readInt() > 0);
setVisibilityToInstantApp(source.readInt());
+ mOrder = source.readInt();
}
private final boolean findMimeType(String type) {
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
new file mode 100644
index 0000000..9f5c877
--- /dev/null
+++ b/core/java/android/content/PermissionChecker.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides permission check APIs that verify both the
+ * permission and the associated app op for this permission if
+ * such is defined.
+ * <p>
+ * In the new permission model permissions with protection level
+ * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
+ * and above the user may not grant such permissions or revoke
+ * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
+ * these permissions are always granted as such apps do not expect
+ * permission revocations and would crash. Therefore, when the
+ * user disables a permission for a legacy app in the UI the
+ * platform disables the APIs guarded by this permission making
+ * them a no-op which is doing nothing or returning an empty
+ * result or default error.
+ * </p>
+ * <p>
+ * It is important that when you perform an operation on behalf of
+ * another app you use these APIs to check for permissions as the
+ * app may be a legacy app that does not participate in the new
+ * permission model for which the user had disabled the "permission"
+ * which is achieved by disallowing the corresponding app op.
+ * </p>
+ *
+ * @hide
+ */
+public final class PermissionChecker {
+ /** Permission result: The permission is granted. */
+ public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED;
+
+ /** Permission result: The permission is denied. */
+ public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED;
+
+ /** Permission result: The permission is denied because the app op is not allowed. */
+ public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1;
+
+ /** @hide */
+ @IntDef({PERMISSION_GRANTED,
+ PERMISSION_DENIED,
+ PERMISSION_DENIED_APP_OP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionResult {}
+
+ private PermissionChecker() {
+ /* do nothing */
+ }
+
+ /**
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkPermission(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, @Nullable String packageName) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ String op = appOpsManager.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (appOpsManager.noteProxyOpNoThrow(op, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+
+ return PERMISSION_GRANTED;
+ }
+
+ /**
+ * Checks whether your app has a given permission and whether the app op
+ * that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ return checkPermission(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName());
+ }
+
+ /**
+ * Checks whether the IPC you are handling has a given permission and whether
+ * the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param packageName The package name making the IPC. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkCallingPermission(@NonNull Context context,
+ @NonNull String permission, @Nullable String packageName) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PERMISSION_DENIED;
+ }
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+
+ /**
+ * Checks whether the IPC you are handling or your app has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkCallingOrSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ String packageName = (Binder.getCallingPid() == Process.myPid())
+ ? context.getPackageName() : null;
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+}
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 3a95a5f..8184361 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -78,8 +78,7 @@
private final Bundle mExtras;
/**
* A flag that indicates that the resolver is aware that an app may match, but would prefer
- * that the installer get the sanitized intent to decide. This should not be used for
- * resolutions that include a host and will be ignored in such cases.
+ * that the installer get the sanitized intent to decide.
*/
private final boolean mShouldLetInstallerDecide;
@@ -96,7 +95,21 @@
this(digest, packageName, filters, versionCode, extras, false);
}
- /** Constructor for intent-based InstantApp resolution results with extras. */
+ /** Constructor for intent-based InstantApp resolution results by hostname. */
+ public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters) {
+ this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+ null /* extras */);
+ }
+
+ /**
+ * Constructor that indicates that resolution could be delegated to the installer when the
+ * sanitized intent contains enough information to resolve completely.
+ */
+ public InstantAppResolveInfo(@Nullable Bundle extras) {
+ this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+ }
+
private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, long versionCode,
@Nullable Bundle extras, boolean shouldLetInstallerDecide) {
@@ -118,21 +131,6 @@
mShouldLetInstallerDecide = shouldLetInstallerDecide;
}
- /** Constructor for intent-based InstantApp resolution results by hostname. */
- public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<InstantAppIntentFilter> filters) {
- this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
- null /* extras */);
- }
-
- /**
- * Constructor that creates a "let the installer decide" response with optional included
- * extras.
- */
- public InstantAppResolveInfo(@Nullable Bundle extras) {
- this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
- }
-
InstantAppResolveInfo(Parcel in) {
mShouldLetInstallerDecide = in.readBoolean();
mExtras = in.readBundle();
@@ -150,7 +148,11 @@
}
}
- /** Returns true if the installer should be notified that it should query for packages. */
+ /**
+ * Returns true if the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide. This should not be true for
+ * resolutions that include a host and will be ignored in such cases.
+ */
public boolean shouldLetInstallerDecide() {
return mShouldLetInstallerDecide;
}
@@ -231,6 +233,11 @@
@SystemApi
public static final class InstantAppDigest implements Parcelable {
static final int DIGEST_MASK = 0xfffff000;
+
+ /**
+ * A special instance that represents and undefined digest used for cases that a host was
+ * not provided or is irrelevant to the response.
+ */
public static final InstantAppDigest UNDEFINED =
new InstantAppDigest(new byte[][]{}, new int[]{});
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 2c0c6ad0..53ffd55 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -43,6 +43,14 @@
*/
public class PackageItemInfo {
private static final float MAX_LABEL_SIZE_PX = 500f;
+
+ private static volatile boolean sForceSafeLabels = false;
+
+ /** {@hide} */
+ public static void setForceSafeLabels(boolean forceSafeLabels) {
+ sForceSafeLabels = forceSafeLabels;
+ }
+
/**
* Public name of this item. From the "android:name" attribute.
*/
@@ -128,7 +136,16 @@
* @return Returns a CharSequence containing the item's label. If the
* item does not have a label, its name is returned.
*/
- public CharSequence loadLabel(PackageManager pm) {
+ public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
+ if (sForceSafeLabels) {
+ return loadSafeLabel(pm);
+ } else {
+ return loadUnsafeLabel(pm);
+ }
+ }
+
+ /** {@hide} */
+ public CharSequence loadUnsafeLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
@@ -163,7 +180,7 @@
@SystemApi
public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
// loadLabel() always returns non-null
- String label = loadLabel(pm).toString();
+ String label = loadUnsafeLabel(pm).toString();
// strip HTML tags to avoid <br> and other tags overwriting original message
String labelStr = Html.fromHtml(label).toString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2420b63..04a028b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3190,7 +3190,7 @@
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
- outError[0] = "<permission> protectionLevel specifies a non-instnat flag but is "
+ outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -3641,7 +3641,9 @@
// getting added to the wrong package.
final CachedComponentArgs cachedArgs = new CachedComponentArgs();
int type;
-
+ boolean hasActivityOrder = false;
+ boolean hasReceiverOrder = false;
+ boolean hasServiceOrder = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3657,6 +3659,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
@@ -3667,6 +3670,7 @@
return false;
}
+ hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
@@ -3676,6 +3680,7 @@
return false;
}
+ hasServiceOrder |= (s.order != 0);
owner.services.add(s);
} else if (tagName.equals("provider")) {
@@ -3694,6 +3699,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
@@ -3827,6 +3833,15 @@
}
}
+ if (hasActivityOrder) {
+ Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+ }
+ if (hasReceiverOrder) {
+ Collections.sort(owner.receivers, (r1, r2) -> Integer.compare(r2.order, r1.order));
+ }
+ if (hasServiceOrder) {
+ Collections.sort(owner.services, (s1, s2) -> Integer.compare(s2.order, s1.order));
+ }
// Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(owner);
@@ -4368,6 +4383,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4678,6 +4694,7 @@
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
info.maxAspectRatio = target.info.maxAspectRatio;
+ info.requestedVrComponent = target.info.requestedVrComponent;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -4745,6 +4762,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4952,6 +4970,7 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -5241,6 +5260,7 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ s.order = Math.max(intent.getOrder(), s.order);
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -5466,6 +5486,10 @@
com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
outInfo.setPriority(priority);
+ int order = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+ outInfo.setOrder(order);
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -7053,6 +7077,8 @@
public Bundle metaData;
public Package owner;
+ /** The order of this component in relation to its peers */
+ public int order;
ComponentName componentName;
String componentShortName;
@@ -7571,6 +7597,7 @@
for (ActivityIntentInfo aii : intents) {
aii.activity = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
@@ -7660,6 +7687,7 @@
for (ServiceIntentInfo aii : intents) {
aii.service = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 2baf539..19b5c45 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -16,10 +16,11 @@
package android.content.res;
+import static android.content.ConfigurationProto.COLOR_MODE;
import static android.content.ConfigurationProto.DENSITY_DPI;
import static android.content.ConfigurationProto.FONT_SCALE;
import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
-import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD;
import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.LOCALES;
import static android.content.ConfigurationProto.MCC;
@@ -33,7 +34,6 @@
import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
import static android.content.ConfigurationProto.TOUCHSCREEN;
import static android.content.ConfigurationProto.UI_MODE;
-import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
import static android.content.ResourcesConfigurationProto.CONFIGURATION;
import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
@@ -1095,11 +1095,9 @@
protoOutputStream.write(MNC, mnc);
mLocaleList.writeToProto(protoOutputStream, LOCALES);
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
- protoOutputStream.write(HDR_COLOR_MODE,
- (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
- protoOutputStream.write(WIDE_COLOR_GAMUT,
- colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+ protoOutputStream.write(COLOR_MODE, colorMode);
protoOutputStream.write(TOUCHSCREEN, touchscreen);
+ protoOutputStream.write(KEYBOARD, keyboard);
protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
protoOutputStream.write(NAVIGATION, navigation);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index f08e1cc..9a276fb 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3535,8 +3535,8 @@
/**
* Gets the focal length (in millimeter) of the camera.
*
- * @return the focal length. This method will always return a valid
- * value.
+ * @return the focal length. Returns -1.0 when the device
+ * doesn't report focal length information.
*/
public float getFocalLength() {
return Float.parseFloat(get(KEY_FOCAL_LENGTH));
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6b2059e..36d5615 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -615,6 +615,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public Point getStableDisplaySize() {
return mGlobal.getStableDisplaySize();
}
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index f85ce3e..e1c69d7 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -33,7 +33,7 @@
*/
@SystemApi
@Deprecated
-public class ContextHubMessage {
+public class ContextHubMessage implements Parcelable {
private static final int DEBUG_LOG_NUM_BYTES = 16;
private int mType;
private int mVersion;
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index b5c01ec..ded1bb8 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -36,7 +36,7 @@
*/
@SystemApi
@Deprecated
-public class NanoApp {
+public class NanoApp implements Parcelable {
private final String TAG = "NanoApp";
private final String UNKNOWN = "Unknown";
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 75a96ee..4d8e734 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -28,7 +28,7 @@
*/
@SystemApi
@Deprecated
-public class NanoAppFilter {
+public class NanoAppFilter implements Parcelable {
private static final String TAG = "NanoAppFilter";
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index f1926eaa..75fb915 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -34,7 +34,7 @@
*/
@SystemApi
@Deprecated
-public class NanoAppInstanceInfo {
+public class NanoAppInstanceInfo implements Parcelable {
private String mPublisher = "Unknown";
private String mName = "Unknown";
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 6e2654e..4631c56 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -25,6 +25,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.Comparator;
/**
* This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
@@ -187,6 +188,20 @@
}
/**
+ * Returns whether the specified prefix is entirely contained in this prefix.
+ *
+ * Note this is mathematical inclusion, so a prefix is always contained within itself.
+ * @param otherPrefix the prefix to test
+ * @hide
+ */
+ public boolean containsPrefix(IpPrefix otherPrefix) {
+ if (otherPrefix.getPrefixLength() < prefixLength) return false;
+ final byte[] otherAddress = otherPrefix.getRawAddress();
+ NetworkUtils.maskRawAddress(otherAddress, prefixLength);
+ return Arrays.equals(otherAddress, address);
+ }
+
+ /**
* @hide
*/
public boolean isIPv6() {
@@ -230,6 +245,38 @@
}
/**
+ * Returns a comparator ordering IpPrefixes by length, shorter to longer.
+ * Contents of the address will break ties.
+ * @hide
+ */
+ public static Comparator<IpPrefix> lengthComparator() {
+ return new Comparator<IpPrefix>() {
+ @Override
+ public int compare(IpPrefix prefix1, IpPrefix prefix2) {
+ if (prefix1.isIPv4()) {
+ if (prefix2.isIPv6()) return -1;
+ } else {
+ if (prefix2.isIPv4()) return 1;
+ }
+ final int p1len = prefix1.getPrefixLength();
+ final int p2len = prefix2.getPrefixLength();
+ if (p1len < p2len) return -1;
+ if (p2len < p1len) return 1;
+ final byte[] a1 = prefix1.address;
+ final byte[] a2 = prefix2.address;
+ final int len = a1.length < a2.length ? a1.length : a2.length;
+ for (int i = 0; i < len; ++i) {
+ if (a1[i] < a2[i]) return -1;
+ if (a1[i] > a2[i]) return 1;
+ }
+ if (a2.length < len) return 1;
+ if (a1.length < len) return -1;
+ return 0;
+ }
+ };
+ }
+
+ /**
* Implement the Parcelable interface.
*/
public static final Creator<IpPrefix> CREATOR =
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 9d94ecc..17b46c6 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -891,7 +891,16 @@
/**
* List of UIDs this network applies to. No restriction if null.
* <p>
- * This is typically (and at this time, only) used by VPN. This network is only available to
+ * For networks, mUids represent the list of network this applies to, and null means this
+ * network applies to all UIDs.
+ * For requests, mUids is the list of UIDs this network MUST apply to to match ; ALL UIDs
+ * must be included in a network so that they match. As an exception to the general rule,
+ * a null mUids field for requests mean "no requirements" rather than what the general rule
+ * would suggest ("must apply to all UIDs") : this is because this has shown to be what users
+ * of this API expect in practice. A network that must match all UIDs can still be
+ * expressed with a set ranging the entire set of possible UIDs.
+ * <p>
+ * mUids is typically (and at this time, only) used by VPN. This network is only available to
* the UIDs in this list, and it is their default network. Apps in this list that wish to
* bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this
* member is null, then the network is not restricted by app UID. If it's an empty list, then
@@ -1013,8 +1022,7 @@
* @hide
*/
public boolean satisfiedByUids(NetworkCapabilities nc) {
- if (null == nc.mUids) return true; // The network satisfies everything.
- if (null == mUids) return false; // Not everything allowed but requires everything
+ if (null == nc.mUids || null == mUids) return true; // The network satisfies everything.
for (UidRange requiredRange : mUids) {
if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
if (!nc.appliesToUidRange(requiredRange)) {
@@ -1227,34 +1235,68 @@
@Override
public String toString() {
- // TODO: enumerate bits for transports and capabilities instead of creating arrays.
- // TODO: use a StringBuilder instead of string concatenation.
- int[] types = getTransportTypes();
- String transports = (types.length > 0) ? " Transports: " + transportNamesOf(types) : "";
-
- types = getCapabilities();
- String capabilities = (types.length > 0 ? " Capabilities: " : "");
- for (int i = 0; i < types.length; ) {
- capabilities += capabilityNameOf(types[i]);
- if (++i < types.length) capabilities += "&";
+ final StringBuilder sb = new StringBuilder("[");
+ if (0 != mTransportTypes) {
+ sb.append(" Transports: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mTransportTypes,
+ NetworkCapabilities::transportNameOf, "|");
+ }
+ if (0 != mNetworkCapabilities) {
+ sb.append(" Capabilities: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
+ NetworkCapabilities::capabilityNameOf, "&");
+ }
+ if (mLinkUpBandwidthKbps > 0) {
+ sb.append(" LinkUpBandwidth>=").append(mLinkUpBandwidthKbps).append("Kbps");
+ }
+ if (mLinkDownBandwidthKbps > 0) {
+ sb.append(" LinkDnBandwidth>=").append(mLinkDownBandwidthKbps).append("Kbps");
+ }
+ if (mNetworkSpecifier != null) {
+ sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
+ }
+ if (hasSignalStrength()) {
+ sb.append(" SignalStrength: ").append(mSignalStrength);
}
- String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
- mLinkUpBandwidthKbps + "Kbps" : "");
- String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
- mLinkDownBandwidthKbps + "Kbps" : "");
+ if (null != mUids) {
+ if ((1 == mUids.size()) && (mUids.valueAt(0).count() == 1)) {
+ sb.append(" Uid: ").append(mUids.valueAt(0).start);
+ } else {
+ sb.append(" Uids: <").append(mUids).append(">");
+ }
+ }
+ if (mEstablishingVpnAppUid != INVALID_UID) {
+ sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
+ }
- String specifier = (mNetworkSpecifier == null ?
- "" : " Specifier: <" + mNetworkSpecifier + ">");
+ sb.append("]");
+ return sb.toString();
+ }
- String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
- String uids = (null != mUids ? " Uids: <" + mUids + ">" : "");
-
- String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid;
-
- return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength
- + uids + establishingAppUid + "]";
+ private interface NameOf {
+ String nameOf(int value);
+ }
+ /**
+ * @hide
+ */
+ public static void appendStringRepresentationOfBitMaskToStringBuilder(StringBuilder sb,
+ long bitMask, NameOf nameFetcher, String separator) {
+ int bitPos = 0;
+ boolean firstElementAdded = false;
+ while (bitMask != 0) {
+ if ((bitMask & 1) != 0) {
+ if (firstElementAdded) {
+ sb.append(separator);
+ } else {
+ firstElementAdded = true;
+ }
+ sb.append(nameFetcher.nameOf(bitPos));
+ }
+ bitMask >>= 1;
+ ++bitPos;
+ }
}
/** @hide */
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index a072409..61199f9 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.text.TextUtils;
import android.util.proto.ProtoOutputStream;
@@ -132,12 +133,18 @@
* needed in terms of {@link NetworkCapabilities} features
*/
public static class Builder {
- private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
+ private final NetworkCapabilities mNetworkCapabilities;
/**
* Default constructor for Builder.
*/
- public Builder() {}
+ public Builder() {
+ // By default, restrict this request to networks available to this app.
+ // Apps can rescind this restriction, but ConnectivityService will enforce
+ // it for apps that do not have the NETWORK_SETTINGS permission.
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.setSingleUid(Process.myUid());
+ }
/**
* Build {@link NetworkRequest} give the current set of capabilities.
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index fe9563d..9a5d502 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,19 +16,20 @@
package android.net;
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Collection;
-import java.util.Locale;
-
import android.os.Parcel;
import android.util.Log;
import android.util.Pair;
+import java.io.FileDescriptor;
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.TreeSet;
/**
* Native methods for managing network interfaces.
@@ -385,4 +386,72 @@
result = builder.toString();
return result;
}
+
+ /**
+ * Returns a prefix set without overlaps.
+ *
+ * This expects the src set to be sorted from shorter to longer. Results are undefined
+ * failing this condition. The returned prefix set is sorted in the same order as the
+ * passed set, with the same comparator.
+ */
+ private static TreeSet<IpPrefix> deduplicatePrefixSet(final TreeSet<IpPrefix> src) {
+ final TreeSet<IpPrefix> dst = new TreeSet<>(src.comparator());
+ // Prefixes match addresses that share their upper part up to their length, therefore
+ // the only kind of possible overlap in two prefixes is strict inclusion of the longer
+ // (more restrictive) in the shorter (including equivalence if they have the same
+ // length).
+ // Because prefixes in the src set are sorted from shorter to longer, deduplicating
+ // is done by simply iterating in order, and not adding any longer prefix that is
+ // already covered by a shorter one.
+ newPrefixes:
+ for (IpPrefix newPrefix : src) {
+ for (IpPrefix existingPrefix : dst) {
+ if (existingPrefix.containsPrefix(newPrefix)) {
+ continue newPrefixes;
+ }
+ }
+ dst.add(newPrefix);
+ }
+ return dst;
+ }
+
+ /**
+ * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set.
+ *
+ * Obviously this returns an integral value between 0 and 2**32.
+ * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the
+ * set is not ordered smallest prefix to longer prefix.
+ *
+ * @param prefixes the set of prefixes, ordered by length
+ */
+ public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) {
+ long routedIPCount = 0;
+ for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+ if (!prefix.isIPv4()) {
+ Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount");
+ }
+ int rank = 32 - prefix.getPrefixLength();
+ routedIPCount += 1L << rank;
+ }
+ return routedIPCount;
+ }
+
+ /**
+ * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set.
+ *
+ * This returns a BigInteger between 0 and 2**128.
+ * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the
+ * set is not ordered smallest prefix to longer prefix.
+ */
+ public static BigInteger routedIPv6AddressCount(final TreeSet<IpPrefix> prefixes) {
+ BigInteger routedIPCount = BigInteger.ZERO;
+ for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+ if (!prefix.isIPv6()) {
+ Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount");
+ }
+ int rank = 128 - prefix.getPrefixLength();
+ routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank));
+ }
+ return routedIPCount;
+ }
}
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index fd465d9..3164929 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -21,8 +21,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.IllegalArgumentException;
-
/**
* An inclusive range of UIDs.
*
@@ -53,6 +51,13 @@
}
/**
+ * Returns the count of UIDs in this range.
+ */
+ public int count() {
+ return 1 + stop - start;
+ }
+
+ /**
* @return {@code true} if this range contains every UID contained by the {@param other} range.
*/
public boolean containsRange(UidRange other) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5e9d39c..9a30487 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2269,27 +2269,27 @@
*/
public abstract int getMobileRadioActiveUnknownCount(int which);
- public static final int DATA_CONNECTION_NONE = 0;
- public static final int DATA_CONNECTION_GPRS = 1;
- public static final int DATA_CONNECTION_EDGE = 2;
- public static final int DATA_CONNECTION_UMTS = 3;
- public static final int DATA_CONNECTION_CDMA = 4;
- public static final int DATA_CONNECTION_EVDO_0 = 5;
- public static final int DATA_CONNECTION_EVDO_A = 6;
- public static final int DATA_CONNECTION_1xRTT = 7;
- public static final int DATA_CONNECTION_HSDPA = 8;
- public static final int DATA_CONNECTION_HSUPA = 9;
- public static final int DATA_CONNECTION_HSPA = 10;
- public static final int DATA_CONNECTION_IDEN = 11;
- public static final int DATA_CONNECTION_EVDO_B = 12;
- public static final int DATA_CONNECTION_LTE = 13;
- public static final int DATA_CONNECTION_EHRPD = 14;
- public static final int DATA_CONNECTION_HSPAP = 15;
- public static final int DATA_CONNECTION_GSM = 16;
- public static final int DATA_CONNECTION_TD_SCDMA = 17;
- public static final int DATA_CONNECTION_IWLAN = 18;
- public static final int DATA_CONNECTION_LTE_CA = 19;
- public static final int DATA_CONNECTION_OTHER = 20;
+ public static final int DATA_CONNECTION_NONE = SystemProto.DataConnection.NONE; // 0
+ public static final int DATA_CONNECTION_GPRS = SystemProto.DataConnection.GPRS; // 1
+ public static final int DATA_CONNECTION_EDGE = SystemProto.DataConnection.EDGE; // 2
+ public static final int DATA_CONNECTION_UMTS = SystemProto.DataConnection.UMTS; // 3
+ public static final int DATA_CONNECTION_CDMA = SystemProto.DataConnection.CDMA; // 4
+ public static final int DATA_CONNECTION_EVDO_0 = SystemProto.DataConnection.EVDO_0; // 5
+ public static final int DATA_CONNECTION_EVDO_A = SystemProto.DataConnection.EVDO_A; // 6
+ public static final int DATA_CONNECTION_1xRTT = SystemProto.DataConnection.ONE_X_RTT; // 7
+ public static final int DATA_CONNECTION_HSDPA = SystemProto.DataConnection.HSDPA; // 8
+ public static final int DATA_CONNECTION_HSUPA = SystemProto.DataConnection.HSUPA; // 9
+ public static final int DATA_CONNECTION_HSPA = SystemProto.DataConnection.HSPA; // 10
+ public static final int DATA_CONNECTION_IDEN = SystemProto.DataConnection.IDEN; // 11
+ public static final int DATA_CONNECTION_EVDO_B = SystemProto.DataConnection.EVDO_B; // 12
+ public static final int DATA_CONNECTION_LTE = SystemProto.DataConnection.LTE; // 13
+ public static final int DATA_CONNECTION_EHRPD = SystemProto.DataConnection.EHRPD; // 14
+ public static final int DATA_CONNECTION_HSPAP = SystemProto.DataConnection.HSPAP; // 15
+ public static final int DATA_CONNECTION_GSM = SystemProto.DataConnection.GSM; // 16
+ public static final int DATA_CONNECTION_TD_SCDMA = SystemProto.DataConnection.TD_SCDMA; // 17
+ public static final int DATA_CONNECTION_IWLAN = SystemProto.DataConnection.IWLAN; // 18
+ public static final int DATA_CONNECTION_LTE_CA = SystemProto.DataConnection.LTE_CA; // 19
+ public static final int DATA_CONNECTION_OTHER = SystemProto.DataConnection.OTHER; // 20
static final String[] DATA_CONNECTION_NAMES = {
"none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 48f5684..6d8831b 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
@@ -287,6 +288,7 @@
* we are operating under, we bump the assumed resource platform version by 1.
* @hide
*/
+ @TestApi
public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
/**
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 76c13be..a93e25a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -479,6 +479,8 @@
/** Initialize a Builder from an existing ThreadPolicy. */
public Builder(ThreadPolicy policy) {
mMask = policy.mask;
+ mListener = policy.mListener;
+ mExecutor = policy.mCallbackExecutor;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2693bab..a9eb360 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,6 +36,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -691,14 +692,13 @@
/**
* Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
* In this case, the system will force-stop the app as if the user chooses the "close app"
- * option on the UI. No feedback report will be collected as there is no way for the user to
- * provide explicit consent.
+ * option on the UI. A feedback report isn't collected as there is no way for the user to
+ * provide explicit consent. The default value is <code>false</code>.
*
- * When this user restriction is set by device owners, it's applied to all users; when it's set
- * by profile owners, it's only applied to the relevant profiles.
- * The default value is <code>false</code>.
+ * <p>When this user restriction is set by device owners, it's applied to all users. When set by
+ * the profile owner of the primary user or a secondary user, the restriction affects only the
+ * calling user. This user restriction has no effect on managed profiles.
*
- * <p>This user restriction has no effect on managed profiles.
* <p>Key for user restrictions.
* <p>Type: Boolean
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
@@ -2612,8 +2612,13 @@
public static int getMaxSupportedUsers() {
// Don't allow multiple users on certain builds
if (android.os.Build.ID.startsWith("JVP")) return 1;
- // Svelte devices don't get multi-user.
- if (ActivityManager.isLowRamDeviceStatic()) return 1;
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ // Low-ram devices are Svelte. Most of the time they don't get multi-user.
+ if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+ != Configuration.UI_MODE_TYPE_TELEVISION) {
+ return 1;
+ }
+ }
return SystemProperties.getInt("fw.max_users",
Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0299407..8fc6901 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10516,8 +10516,13 @@
* entity_list_default use ":" as delimiter for values. Ex:
*
* <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
+ * smart_linkify_enabled (boolean)
+ * system_textclassifier_enabled (boolean)
+ * model_dark_launch_enabled (boolean)
+ * smart_selection_enabled (boolean)
+ * smart_text_share_enabled (boolean)
+ * smart_linkify_enabled (boolean)
+ * smart_select_animation_enabled (boolean)
* suggest_selection_max_range_length (int)
* classify_text_max_range_length (int)
* generate_links_max_text_length (int)
@@ -10530,7 +10535,7 @@
* <p>
* Type: string
* @hide
- * see also android.view.textclassifier.TextClassifierConstants
+ * see also android.view.textclassifier.TextClassificationConstants
*/
public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
@@ -10590,6 +10595,23 @@
= "forced_app_standby_for_small_battery_enabled";
/**
+ * Whether or not to enable the Off Body, Radios Off feature on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
+ = "off_body_radios_off_for_small_battery_enabled";
+
+ /**
+ * How long after the device goes off body to disable radios, in milliseconds.
+ * Type: long
+ * Default: 10 minutes
+ * @hide
+ */
+ public static final String OFF_BODY_RADIOS_OFF_DELAY_MS = "off_body_radios_off_delay_ms";
+
+ /**
* Whether or not to enable Time Only Mode for watch type devices.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -11446,6 +11468,7 @@
*
* @hide
*/
+ @TestApi
public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
"hidden_api_blacklist_exemptions";
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 6c882e1..4881375 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -99,7 +99,11 @@
public static final int ERROR_SESSION_EXPIRED = 24;
/**
- * Failed because the provided certificate was not a valid X509 certificate.
+ * Failed because the format of the provided certificate is incorrect, e.g., cannot be decoded
+ * properly or misses necessary fields.
+ *
+ * <p>Note that this is different from {@link #ERROR_INVALID_CERTIFICATE}, which implies the
+ * certificate has a correct format but cannot be validated.
*
* @hide
*/
@@ -121,6 +125,16 @@
*/
public static final int ERROR_INVALID_KEY_FORMAT = 27;
+ /**
+ * Failed because the provided certificate cannot be validated, e.g., is expired or has invalid
+ * signatures.
+ *
+ * <p>Note that this is different from {@link #ERROR_BAD_CERTIFICATE_FORMAT}, which denotes
+ * incorrect certificate formats, e.g., due to wrong encoding or structure.
+ *
+ * @hide
+ */
+ public static final int ERROR_INVALID_CERTIFICATE = 28;
private final ILockSettings mBinder;
private final KeyStore mKeyStore;
@@ -149,23 +163,9 @@
}
/**
- * Initializes key recovery service for the calling application. RecoveryController
- * randomly chooses one of the keys from the list and keeps it to use for future key export
- * operations. Collection of all keys in the list must be signed by the provided {@code
- * rootCertificateAlias}, which must also be present in the list of root certificates
- * preinstalled on the device. The random selection allows RecoveryController to select
- * which of a set of remote recovery service devices will be used.
- *
- * <p>In addition, RecoveryController enforces a delay of three months between
- * consecutive initialization attempts, to limit the ability of an attacker to often switch
- * remote recovery devices and significantly increase number of recovery attempts.
- *
- * @param rootCertificateAlias alias of a root certificate preinstalled on the device
- * @param signedPublicKeyList binary blob a list of X509 certificates and signature
- * @throws CertificateException if the {@code signedPublicKeyList} is in a bad format.
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
+ * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
public void initRecoveryService(
@NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
@@ -175,7 +175,54 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
- if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
+ if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT
+ || e.errorCode == ERROR_INVALID_CERTIFICATE) {
+ throw new CertificateException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Initializes the recovery service for the calling application. The detailed steps should be:
+ * <ol>
+ * <li>Parse {@code signatureFile} to get relevant information.
+ * <li>Validate the signer's X509 certificate, contained in {@code signatureFile}, against
+ * the root certificate pre-installed in the OS and chosen by {@code
+ * rootCertificateAlias}.
+ * <li>Verify the public-key signature, contained in {@code signatureFile}, and verify it
+ * against the entire {@code certificateFile}.
+ * <li>Parse {@code certificateFile} to get relevant information.
+ * <li>Check the serial number, contained in {@code certificateFile}, and skip the following
+ * steps if the serial number is not larger than the one previously stored.
+ * <li>Randomly choose a X509 certificate from the endpoint X509 certificates, contained in
+ * {@code certificateFile}, and validate it against the root certificate pre-installed
+ * in the OS and chosen by {@code rootCertificateAlias}.
+ * <li>Store the chosen X509 certificate and the serial in local database for later use.
+ * </ol>
+ *
+ * @param rootCertificateAlias the alias of a root certificate pre-installed in the OS
+ * @param certificateFile the binary content of the XML file containing a list of recovery
+ * service X509 certificates, and other metadata including the serial number
+ * @param signatureFile the binary content of the XML file containing the public-key signature
+ * of the entire certificate file, and a signer's X509 certificate
+ * @throws CertificateException if the given certificate files cannot be parsed or validated
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+ public void initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] certificateFile,
+ @NonNull byte[] signatureFile)
+ throws CertificateException, InternalRecoveryServiceException {
+ try {
+ mBinder.initRecoveryServiceWithSigFile(
+ rootCertificateAlias, certificateFile, signatureFile);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT
+ || e.errorCode == ERROR_INVALID_CERTIFICATE) {
throw new CertificateException(e.getMessage());
}
throw wrapUnexpectedServiceSpecificException(e);
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 2b627b4..137dd89 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -94,7 +94,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
- if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
+ if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+ || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
throw new CertificateException(e.getMessage());
}
throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
@@ -143,7 +144,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
- if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
+ if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+ || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
throw new CertificateException(e.getMessage());
}
throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 674f809..70dfef5 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -20,7 +20,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.PermissionChecker;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -174,8 +174,8 @@
*/
private boolean checkPermissions(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "checkPermissions");
- if (RecognitionService.this.checkCallingOrSelfPermission(android.Manifest.permission.
- RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
+ if (PermissionChecker.checkCallingOrSelfPermission(this,
+ android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {
return true;
}
try {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index eba9129..94025ef 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -159,9 +159,9 @@
private static Locale sIs24HourLocale;
private static boolean sIs24Hour;
-
/**
- * Returns true if user preference is set to 24-hour format.
+ * Returns true if times should be formatted as 24 hour times, false if times should be
+ * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
* @param context the context to use for the content resolver
* @return true if 24 hour time format is selected, false otherwise.
*/
@@ -170,7 +170,8 @@
}
/**
- * Returns true if user preference with the given user handle is set to 24-hour format.
+ * Returns true if times should be formatted as 24 hour times, false if times should be
+ * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
* @param context the context to use for the content resolver
* @param userHandle the user handle of the user to query.
* @return true if 24 hour time format is selected, false otherwise.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4adcb8f..914ba0c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -185,13 +185,6 @@
*/
void setScreenCaptureDisabled(int userId, boolean disabled);
- /**
- * Testing and debugging infrastructure for writing surface events
- * to given FD. See RemoteSurfaceTrace.java or Wm.java for format.
- */
- void enableSurfaceTrace(in ParcelFileDescriptor fd);
- void disableSurfaceTrace();
-
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index fbba8ab..137e820 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
@@ -25,6 +26,7 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -53,6 +55,10 @@
private ImageView mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
+ private View mOverlayIcon;
+ private View mCameraIcon;
+ private View mMicIcon;
+ private View mAppOps;
private int mIconColor;
private int mOriginalNotificationColor;
private boolean mExpanded;
@@ -108,6 +114,10 @@
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+ mCameraIcon = findViewById(com.android.internal.R.id.camera);
+ mMicIcon = findViewById(com.android.internal.R.id.mic);
+ mOverlayIcon = findViewById(com.android.internal.R.id.overlay);
+ mAppOps = findViewById(com.android.internal.R.id.app_ops);
}
@Override
@@ -198,6 +208,11 @@
layoutRight = end - paddingEnd;
end = layoutLeft = layoutRight - child.getMeasuredWidth();
}
+ if (child == mAppOps) {
+ int paddingEnd = mContentEndMargin;
+ layoutRight = end - paddingEnd;
+ end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ }
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
int ltrLeft = layoutLeft;
layoutLeft = getWidth() - layoutRight;
@@ -289,6 +304,22 @@
updateExpandButton();
}
+ /**
+ * Shows or hides 'app op in use' icons based on app usage.
+ */
+ public void showAppOpsIcons(ArraySet<Integer> appOps) {
+ if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null) {
+ return;
+ }
+
+ mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ ? View.VISIBLE : View.GONE);
+ mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
+ ? View.VISIBLE : View.GONE);
+ mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
+ ? View.VISIBLE : View.GONE);
+ }
+
private void updateExpandButton() {
int drawableId;
int contentDescriptionId;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bd7f8e5..b7524fb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -152,6 +152,7 @@
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
+ private static native void nativeDestroy(long transactionObj, long nativeObject);
private static native IBinder nativeGetHandle(long nativeObject);
private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -763,18 +764,14 @@
}
public void deferTransactionUntil(IBinder handle, long frame) {
- if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntil(this, handle, frame);
- }
+ synchronized(SurfaceControl.class) {
+ sGlobalTransaction.deferTransactionUntil(this, handle, frame);
}
}
public void deferTransactionUntil(Surface barrier, long frame) {
- if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
- }
+ synchronized(SurfaceControl.class) {
+ sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
}
}
@@ -1479,6 +1476,9 @@
public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
long frameNumber) {
+ if (frameNumber < 0) {
+ return this;
+ }
sc.checkNotReleased();
nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
return this;
@@ -1486,6 +1486,9 @@
public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
long frameNumber) {
+ if (frameNumber < 0) {
+ return this;
+ }
sc.checkNotReleased();
nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
barrierSurface.mNativeObject, frameNumber);
@@ -1570,6 +1573,16 @@
return this;
}
+ /**
+ * Same as {@link #destroy()} except this is invoked in a transaction instead of
+ * immediately.
+ */
+ public Transaction destroy(SurfaceControl sc) {
+ sc.checkNotReleased();
+ nativeDestroy(mNativeObject, sc.mNativeObject);
+ return this;
+ }
+
public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index db19681..e996ea1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -20,11 +20,13 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
+import android.os.Build;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -1066,6 +1068,9 @@
mInitialized = true;
mAppContext = context.getApplicationContext();
+ nSetDebuggingEnabled(
+ (mAppContext.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
+ || Build.IS_DEBUGGABLE);
initSched(renderProxy);
initGraphicsStats();
}
@@ -1194,4 +1199,5 @@
private static native void nSetHighContrastText(boolean enabled);
// For temporary experimentation b/66945974
private static native void nHackySetRTAnimationsEnabled(boolean enabled);
+ private static native void nSetDebuggingEnabled(boolean enabled);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f61b652..e285222 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2953,6 +2953,9 @@
* 1 PFLAG3_NO_REVEAL_ON_FOCUS
* 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT
* 1 PFLAG3_SCREEN_READER_FOCUSABLE
+ * 1 PFLAG3_AGGREGATED_VISIBLE
+ * 1 PFLAG3_AUTOFILLID_EXPLICITLY_SET
+ * 1 available
* |-------|-------|-------|-------|
*/
@@ -3243,6 +3246,12 @@
*/
private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
+ /**
+ * Used to indicate that {@link #mAutofillId} was explicitly set through
+ * {@link #setAutofillId(AutofillId)}.
+ */
+ private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -8205,16 +8214,28 @@
* @throws IllegalArgumentException if the id is an autofill id associated with a virtual view.
*/
public void setAutofillId(@Nullable AutofillId id) {
+ // TODO(b/37566627): add unit / CTS test for all possible combinations below
if (android.view.autofill.Helper.sVerbose) {
Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
}
if (isAttachedToWindow()) {
throw new IllegalStateException("Cannot set autofill id when view is attached");
}
- if (id.isVirtual()) {
+ if (id != null && id.isVirtual()) {
throw new IllegalStateException("Cannot set autofill id assigned to virtual views");
}
+ if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) {
+ // Ignore reset because it was never explicitly set before.
+ return;
+ }
mAutofillId = id;
+ if (id != null) {
+ mAutofillViewId = id.getViewId();
+ mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+ } else {
+ mAutofillViewId = NO_ID;
+ mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+ }
}
/**
@@ -18524,7 +18545,17 @@
// Hence prevent the same autofill view id from being restored multiple times.
((BaseSavedState) state).mSavedData &= ~BaseSavedState.AUTOFILL_ID;
- mAutofillViewId = baseState.mAutofillViewId;
+ if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) {
+ // Ignore when view already set it through setAutofillId();
+ if (android.view.autofill.Helper.sDebug) {
+ Log.d(VIEW_LOG_TAG, "onRestoreInstanceState(): not setting autofillId to "
+ + baseState.mAutofillViewId + " because view explicitly set it to "
+ + mAutofillId);
+ }
+ } else {
+ mAutofillViewId = baseState.mAutofillViewId;
+ mAutofillId = null; // will be set on demand by getAutofillId()
+ }
}
}
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index c5a94da..7a9de45 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -303,6 +303,7 @@
private final long mGlobalActionsKeyTimeout;
private final float mVerticalScrollFactor;
private final float mHorizontalScrollFactor;
+ private final boolean mShowMenuShortcutsWhenKeyboardPresent;
private boolean sHasPermanentMenuKey;
private boolean sHasPermanentMenuKeySet;
@@ -335,6 +336,7 @@
mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT;
mHorizontalScrollFactor = HORIZONTAL_SCROLL_FACTOR;
mVerticalScrollFactor = VERTICAL_SCROLL_FACTOR;
+ mShowMenuShortcutsWhenKeyboardPresent = false;
}
/**
@@ -428,6 +430,10 @@
com.android.internal.R.dimen.config_horizontalScrollFactor);
mVerticalScrollFactor = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_verticalScrollFactor);
+
+ mShowMenuShortcutsWhenKeyboardPresent = res.getBoolean(
+ com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent);
+
}
/**
@@ -910,6 +916,15 @@
}
/**
+ * Check if shortcuts should be displayed in menus.
+ *
+ * @return {@code True} if shortcuts should be displayed in menus.
+ */
+ public boolean shouldShowMenuShortcutsWhenKeyboardPresent() {
+ return mShowMenuShortcutsWhenKeyboardPresent;
+ }
+
+ /**
* @hide
* @return Whether or not marquee should use fading edges.
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 01d9265..95e4abb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -95,6 +95,7 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
+import android.view.autofill.AutofillManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -4781,6 +4782,21 @@
ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
}
+ if (action == MotionEvent.ACTION_DOWN && mView instanceof ViewGroup) {
+ // Upon motion event within app window, close autofill ui.
+ ViewGroup decorView = (ViewGroup) mView;
+ if (decorView.getChildCount() > 0) {
+ // We cannot use decorView's Context for querying AutofillManager: DecorView's
+ // context is based on Application Context, it would allocate a different
+ // AutofillManager instance.
+ AutofillManager afm = (AutofillManager) decorView.getChildAt(0).getContext()
+ .getSystemService(Context.AUTOFILL_MANAGER_SERVICE);
+ if (afm != null) {
+ afm.requestHideFillUi();
+ }
+ }
+ }
+
if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
@@ -6440,18 +6456,24 @@
params.backup();
mTranslator.translateWindowLayout(params);
}
+
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
- }
- if (params != null && mOrigWindowType != params.type) {
- // For compatibility with old apps, don't crash here.
- if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Slog.w(mTag, "Window type can not be changed after "
- + "the window is added; ignoring change of " + mView);
- params.type = mOrigWindowType;
+ if (mOrigWindowType != params.type) {
+ // For compatibility with old apps, don't crash here.
+ if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ Slog.w(mTag, "Window type can not be changed after "
+ + "the window is added; ignoring change of " + mView);
+ params.type = mOrigWindowType;
+ }
+ }
+
+ if (mSurface.isValid()) {
+ params.frameNumber = mSurface.getNextFrameNumber();
}
}
+
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c0a9666..2354f25 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2234,6 +2234,7 @@
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
* @see DisplayCutout
+ * @see android.R.attr#layoutInDisplayCutoutMode
*/
@LayoutInDisplayCutoutMode
public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
@@ -2371,6 +2372,13 @@
public long hideTimeoutMilliseconds = -1;
/**
+ * A frame number in which changes requested in this layout will be rendered.
+ *
+ * @hide
+ */
+ public long frameNumber = -1;
+
+ /**
* The color mode requested by this window. The target display may
* not be able to honor the request. When the color mode is not set
* to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2543,6 +2551,7 @@
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
out.writeLong(hideTimeoutMilliseconds);
+ out.writeLong(frameNumber);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2599,6 +2608,7 @@
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
hideTimeoutMilliseconds = in.readLong();
+ frameNumber = in.readLong();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2799,6 +2809,10 @@
changes |= SURFACE_INSETS_CHANGED;
}
+ // The frame number changing is only relevant in the context of other
+ // changes, and so we don't need to track it with a flag.
+ frameNumber = o.frameNumber;
+
if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
hasManualSurfaceInsets = o.hasManualSurfaceInsets;
changes |= SURFACE_INSETS_CHANGED;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index a4261eb..1e562ea 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -358,6 +358,9 @@
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
+ /** id of last requested autofill ui */
+ @Nullable private AutofillId mIdShownFillUi;
+
/**
* Views that were already "entered" - if they're entered again when the session is not active,
* they're ignored
@@ -1547,6 +1550,7 @@
mTrackedViews = null;
mFillableIds = null;
mSaveTriggerId = null;
+ mIdShownFillUi = null;
if (resetEnteredIds) {
mEnteredIds = null;
}
@@ -1676,8 +1680,9 @@
if (client != null) {
if (client.autofillClientRequestShowFillUi(anchor, width, height,
- anchorBounds, presenter) && mCallback != null) {
+ anchorBounds, presenter)) {
callback = mCallback;
+ mIdShownFillUi = id;
}
}
}
@@ -1944,10 +1949,23 @@
}
}
- private void requestHideFillUi(AutofillId id) {
- final View anchor = findView(id);
+ /** @hide */
+ public void requestHideFillUi() {
+ requestHideFillUi(mIdShownFillUi, true);
+ }
+
+ private void requestHideFillUi(AutofillId id, boolean force) {
+ final View anchor = id == null ? null : findView(id);
if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
if (anchor == null) {
+ if (force) {
+ // When user taps outside autofill window, force to close fill ui even id does
+ // not match.
+ AutofillClient client = getClient();
+ if (client != null) {
+ client.autofillClientRequestHideFillUi();
+ }
+ }
return;
}
requestHideFillUi(id, anchor);
@@ -1963,7 +1981,8 @@
// service being uninstalled and the UI being dismissed.
AutofillClient client = getClient();
if (client != null) {
- if (client.autofillClientRequestHideFillUi() && mCallback != null) {
+ if (client.autofillClientRequestHideFillUi()) {
+ mIdShownFillUi = null;
callback = mCallback;
}
}
@@ -2655,7 +2674,7 @@
public void requestHideFillUi(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.requestHideFillUi(id));
+ afm.post(() -> afm.requestHideFillUi(id, false));
}
}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 1789edf..2b335fb 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -29,6 +29,8 @@
import android.service.textclassifier.ITextLinksCallback;
import android.service.textclassifier.ITextSelectionCallback;
+import com.android.internal.util.Preconditions;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -40,13 +42,16 @@
private static final String LOG_TAG = "SystemTextClassifier";
private final ITextClassifierService mManagerService;
+ private final TextClassificationConstants mSettings;
private final TextClassifier mFallback;
private final String mPackageName;
- SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException {
+ SystemTextClassifier(Context context, TextClassificationConstants settings)
+ throws ServiceManager.ServiceNotFoundException {
mManagerService = ITextClassifierService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
- mFallback = new TextClassifierImpl(context);
+ mSettings = Preconditions.checkNotNull(settings);
+ mFallback = new TextClassifierImpl(context, settings);
mPackageName = context.getPackageName();
}
@@ -108,6 +113,11 @@
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Utils.validate(text, false /* allowInMainThread */);
+
+ if (!mSettings.isSmartLinkifyEnabled()) {
+ return TextClassifier.NO_OP.generateLinks(text, options);
+ }
+
try {
if (options == null) {
options = new TextLinks.Options().setCallingPackageName(mPackageName);
diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
similarity index 62%
rename from core/java/android/view/textclassifier/TextClassifierConstants.java
rename to core/java/android/view/textclassifier/TextClassificationConstants.java
index 397473b..21b5603 100644
--- a/core/java/android/view/textclassifier/TextClassifierConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -30,11 +30,17 @@
* This is encoded as a key=value list, separated by commas. Ex:
*
* <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
+ * smart_linkify_enabled (boolean)
+ * system_textclassifier_enabled (boolean)
+ * model_dark_launch_enabled (boolean)
+ * smart_selection_enabled (boolean)
+ * smart_text_share_enabled (boolean)
+ * smart_linkify_enabled (boolean)
+ * smart_select_animation_enabled (boolean)
* suggest_selection_max_range_length (int)
* classify_text_max_range_length (int)
* generate_links_max_text_length (int)
+ * generate_links_log_sample_rate (int)
* entity_list_default (String[])
* entity_list_not_editable (String[])
* entity_list_editable (String[])
@@ -46,20 +52,28 @@
*
* Example of setting the values for testing.
* adb shell settings put global text_classifier_constants \
- * smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true,\
+ * model_dark_launch_enabled=true,smart_selection_enabled=true,\
* entity_list_default=phone:address
* @hide
*/
-public final class TextClassifierConstants {
+public final class TextClassificationConstants {
- private static final String LOG_TAG = "TextClassifierConstants";
+ private static final String LOG_TAG = "TextClassificationConstants";
- private static final String SMART_SELECTION_DARK_LAUNCH =
- "smart_selection_dark_launch";
- private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
- "smart_selection_enabled_for_edit_text";
+ private static final String LOCAL_TEXT_CLASSIFIER_ENABLED =
+ "local_textclassifier_enabled";
+ private static final String SYSTEM_TEXT_CLASSIFIER_ENABLED =
+ "system_textclassifier_enabled";
+ private static final String MODEL_DARK_LAUNCH_ENABLED =
+ "model_dark_launch_enabled";
+ private static final String SMART_SELECTION_ENABLED =
+ "smart_selection_enabled";
+ private static final String SMART_TEXT_SHARE_ENABLED =
+ "smart_text_share_enabled";
private static final String SMART_LINKIFY_ENABLED =
"smart_linkify_enabled";
+ private static final String SMART_SELECT_ANIMATION_ENABLED =
+ "smart_select_animation_enabled";
private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
"suggest_selection_max_range_length";
private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
@@ -75,9 +89,13 @@
private static final String ENTITY_LIST_EDITABLE =
"entity_list_editable";
- private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
- private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
+ private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
+ private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
+ private static final boolean MODEL_DARK_LAUNCH_ENABLED_DEFAULT = false;
+ private static final boolean SMART_SELECTION_ENABLED_DEFAULT = true;
+ private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
+ private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
@@ -92,12 +110,13 @@
.add(TextClassifier.TYPE_DATE_TIME)
.add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
- /** Default settings. */
- static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
- private final boolean mDarkLaunch;
- private final boolean mSuggestSelectionEnabledForEditableText;
+ private final boolean mSystemTextClassifierEnabled;
+ private final boolean mLocalTextClassifierEnabled;
+ private final boolean mModelDarkLaunchEnabled;
+ private final boolean mSmartSelectionEnabled;
+ private final boolean mSmartTextShareEnabled;
private final boolean mSmartLinkifyEnabled;
+ private final boolean mSmartSelectionAnimationEnabled;
private final int mSuggestSelectionMaxRangeLength;
private final int mClassifyTextMaxRangeLength;
private final int mGenerateLinksMaxTextLength;
@@ -106,20 +125,7 @@
private final List<String> mEntityListNotEditable;
private final List<String> mEntityListEditable;
- private TextClassifierConstants() {
- mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
- mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
- mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
- mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
- mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
- mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
- mGenerateLinksLogSampleRate = GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT;
- mEntityListDefault = parseEntityList(ENTITY_LIST_DEFAULT_VALUE);
- mEntityListNotEditable = mEntityListDefault;
- mEntityListEditable = mEntityListDefault;
- }
-
- private TextClassifierConstants(@Nullable String settings) {
+ private TextClassificationConstants(@Nullable String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
try {
parser.setString(settings);
@@ -127,15 +133,27 @@
// Failed to parse the settings string, log this and move on with defaults.
Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
}
- mDarkLaunch = parser.getBoolean(
- SMART_SELECTION_DARK_LAUNCH,
- SMART_SELECTION_DARK_LAUNCH_DEFAULT);
- mSuggestSelectionEnabledForEditableText = parser.getBoolean(
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
+ mSystemTextClassifierEnabled = parser.getBoolean(
+ SYSTEM_TEXT_CLASSIFIER_ENABLED,
+ SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+ mLocalTextClassifierEnabled = parser.getBoolean(
+ LOCAL_TEXT_CLASSIFIER_ENABLED,
+ LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+ mModelDarkLaunchEnabled = parser.getBoolean(
+ MODEL_DARK_LAUNCH_ENABLED,
+ MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+ mSmartSelectionEnabled = parser.getBoolean(
+ SMART_SELECTION_ENABLED,
+ SMART_SELECTION_ENABLED_DEFAULT);
+ mSmartTextShareEnabled = parser.getBoolean(
+ SMART_TEXT_SHARE_ENABLED,
+ SMART_TEXT_SHARE_ENABLED_DEFAULT);
mSmartLinkifyEnabled = parser.getBoolean(
SMART_LINKIFY_ENABLED,
SMART_LINKIFY_ENABLED_DEFAULT);
+ mSmartSelectionAnimationEnabled = parser.getBoolean(
+ SMART_SELECT_ANIMATION_ENABLED,
+ SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
mSuggestSelectionMaxRangeLength = parser.getInt(
SUGGEST_SELECTION_MAX_RANGE_LENGTH,
SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
@@ -160,22 +178,38 @@
}
/** Load from a settings string. */
- public static TextClassifierConstants loadFromString(String settings) {
- return new TextClassifierConstants(settings);
+ public static TextClassificationConstants loadFromString(String settings) {
+ return new TextClassificationConstants(settings);
}
- public boolean isDarkLaunch() {
- return mDarkLaunch;
+ public boolean isLocalTextClassifierEnabled() {
+ return mLocalTextClassifierEnabled;
}
- public boolean isSuggestSelectionEnabledForEditableText() {
- return mSuggestSelectionEnabledForEditableText;
+ public boolean isSystemTextClassifierEnabled() {
+ return mSystemTextClassifierEnabled;
+ }
+
+ public boolean isModelDarkLaunchEnabled() {
+ return mModelDarkLaunchEnabled;
+ }
+
+ public boolean isSmartSelectionEnabled() {
+ return mSmartSelectionEnabled;
+ }
+
+ public boolean isSmartTextShareEnabled() {
+ return mSmartTextShareEnabled;
}
public boolean isSmartLinkifyEnabled() {
return mSmartLinkifyEnabled;
}
+ public boolean isSmartSelectionAnimationEnabled() {
+ return mSmartSelectionAnimationEnabled;
+ }
+
public int getSuggestSelectionMaxRangeLength() {
return mSuggestSelectionMaxRangeLength;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 300aef2..a7f1ca1 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -20,8 +20,11 @@
import android.annotation.SystemService;
import android.content.Context;
import android.os.ServiceManager;
+import android.provider.Settings;
import android.service.textclassifier.TextClassifierService;
+import android.view.textclassifier.TextClassifier.TextClassifierType;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
/**
@@ -30,55 +33,41 @@
@SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
public final class TextClassificationManager {
- // TODO: Make this a configurable flag.
- private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED = true;
-
private static final String LOG_TAG = "TextClassificationManager";
private final Object mLock = new Object();
private final Context mContext;
+ private final TextClassificationConstants mSettings;
+
+ @GuardedBy("mLock")
private TextClassifier mTextClassifier;
+ @GuardedBy("mLock")
+ private TextClassifier mLocalTextClassifier;
+ @GuardedBy("mLock")
private TextClassifier mSystemTextClassifier;
/** @hide */
public TextClassificationManager(Context context) {
mContext = Preconditions.checkNotNull(context);
+ mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
+ context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
}
/**
- * Returns the system's default TextClassifier.
- * @hide
- */
- // TODO: Unhide when this is ready.
- public TextClassifier getSystemDefaultTextClassifier() {
- synchronized (mLock) {
- if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
- try {
- Log.d(LOG_TAG, "Initialized SystemTextClassifier");
- mSystemTextClassifier = new SystemTextClassifier(mContext);
- } catch (ServiceManager.ServiceNotFoundException e) {
- Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
- }
- }
- if (mSystemTextClassifier == null) {
- Log.d(LOG_TAG, "Using an in-process TextClassifier as the system default");
- mSystemTextClassifier = new TextClassifierImpl(mContext);
- }
- }
- return mSystemTextClassifier;
- }
-
- /**
- * Returns the text classifier.
+ * Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
+ * If this is null, this method returns a default text classifier (i.e. either the system text
+ * classifier if one exists, or a local text classifier running in this app.)
+ *
+ * @see #setTextClassifier(TextClassifier)
*/
public TextClassifier getTextClassifier() {
synchronized (mLock) {
if (mTextClassifier == null) {
if (isSystemTextClassifierEnabled()) {
- mTextClassifier = getSystemDefaultTextClassifier();
+ mTextClassifier = getSystemTextClassifier();
} else {
- mTextClassifier = new TextClassifierImpl(mContext);
+ mTextClassifier = getLocalTextClassifier();
}
}
return mTextClassifier;
@@ -96,8 +85,75 @@
}
}
+ /**
+ * Returns a specific type of text classifier.
+ * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
+ *
+ * @see TextClassifier#LOCAL
+ * @see TextClassifier#SYSTEM
+ * @hide
+ */
+ // TODO: Expose as system API.
+ public TextClassifier getTextClassifier(@TextClassifierType int type) {
+ switch (type) {
+ case TextClassifier.LOCAL:
+ return getLocalTextClassifier();
+ default:
+ return getSystemTextClassifier();
+ }
+ }
+
+ /** @hide */
+ public TextClassificationConstants getSettings() {
+ return mSettings;
+ }
+
+ private TextClassifier getSystemTextClassifier() {
+ synchronized (mLock) {
+ if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+ try {
+ mSystemTextClassifier = new SystemTextClassifier(mContext, mSettings);
+ Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
+ }
+ }
+ }
+ if (mSystemTextClassifier != null) {
+ return mSystemTextClassifier;
+ }
+ return TextClassifier.NO_OP;
+ }
+
+ private TextClassifier getLocalTextClassifier() {
+ synchronized (mLock) {
+ if (mLocalTextClassifier == null) {
+ if (mSettings.isLocalTextClassifierEnabled()) {
+ mLocalTextClassifier = new TextClassifierImpl(mContext, mSettings);
+ } else {
+ Log.d(LOG_TAG, "Local TextClassifier disabled");
+ mLocalTextClassifier = TextClassifierImpl.NO_OP;
+ }
+ }
+ return mLocalTextClassifier;
+ }
+ }
+
private boolean isSystemTextClassifierEnabled() {
- return SYSTEM_TEXT_CLASSIFIER_ENABLED
+ return mSettings.isSystemTextClassifierEnabled()
&& TextClassifierService.getServiceComponentName(mContext) != null;
}
+
+ /** @hide */
+ public static TextClassificationConstants getSettings(Context context) {
+ Preconditions.checkNotNull(context);
+ final TextClassificationManager tcm =
+ context.getSystemService(TextClassificationManager.class);
+ if (tcm != null) {
+ return tcm.mSettings;
+ } else {
+ return TextClassificationConstants.loadFromString(Settings.Global.getString(
+ context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+ }
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index d52a30b..ec40fdd 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,6 +16,7 @@
package android.view.textclassifier;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -49,6 +50,16 @@
/** @hide */
String DEFAULT_LOG_TAG = "androidtc";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {LOCAL, SYSTEM})
+ @interface TextClassifierType {} // TODO: Expose as system APIs.
+ /** Specifies a TextClassifier that runs locally in the app's process. @hide */
+ int LOCAL = 0;
+ /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
+ int SYSTEM = 1;
+
/** The TextClassifier failed to run. */
String TYPE_UNKNOWN = "";
/** The classifier ran, but didn't recognize a known entity. */
@@ -329,14 +340,6 @@
}
/**
- * Returns this TextClassifier's settings.
- * @hide
- */
- default TextClassifierConstants getSettings() {
- return TextClassifierConstants.DEFAULT;
- }
-
- /**
* Configuration object for specifying what entities to identify.
*
* Configs are initially based on a predefined preset, and can be modified from there.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 5b7095b..41f1c69 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -34,7 +34,6 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.provider.Settings;
import android.view.textclassifier.logging.DefaultLogger;
import android.view.textclassifier.logging.GenerateLinksLogger;
import android.view.textclassifier.logging.Logger;
@@ -99,13 +98,13 @@
@GuardedBy("mLoggerLock") // Do not access outside this lock.
private Logger mLogger; // Should never be null if mLoggerConfig.get() is not null.
- private TextClassifierConstants mSettings;
+ private final TextClassificationConstants mSettings;
- public TextClassifierImpl(Context context) {
+ public TextClassifierImpl(Context context, TextClassificationConstants settings) {
mContext = Preconditions.checkNotNull(context);
mFallback = TextClassifier.NO_OP;
- mGenerateLinksLogger = new GenerateLinksLogger(
- getSettings().getGenerateLinksLogSampleRate());
+ mSettings = Preconditions.checkNotNull(settings);
+ mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
}
/** @inheritDoc */
@@ -117,7 +116,7 @@
try {
final int rangeLength = selectionEndIndex - selectionStartIndex;
if (text.length() > 0
- && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
+ && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
final Calendar refTime = Calendar.getInstance();
@@ -126,7 +125,7 @@
final String string = text.toString();
final int start;
final int end;
- if (getSettings().isDarkLaunch() && !darkLaunchAllowed) {
+ if (mSettings.isModelDarkLaunchEnabled() && !darkLaunchAllowed) {
start = selectionStartIndex;
end = selectionEndIndex;
} else {
@@ -179,7 +178,7 @@
Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
try {
final int rangeLength = endIndex - startIndex;
- if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
+ if (text.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
final String string = text.toString();
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
@@ -214,7 +213,7 @@
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
- if (!getSettings().isSmartLinkifyEnabled()) {
+ if (!mSettings.isSmartLinkifyEnabled()) {
return builder.build();
}
@@ -226,7 +225,7 @@
options != null && options.getEntityConfig() != null
? options.getEntityConfig().resolveEntityListModifications(
getEntitiesForHints(options.getEntityConfig().getHints()))
- : getSettings().getEntityListDefault();
+ : mSettings.getEntityListDefault();
final TextClassifierImplNative nativeImpl =
getNative(defaultLocales);
final TextClassifierImplNative.AnnotatedSpan[] annotations =
@@ -268,7 +267,7 @@
/** @inheritDoc */
@Override
public int getMaxGenerateLinksTextLength() {
- return getSettings().getGenerateLinksMaxTextLength();
+ return mSettings.getGenerateLinksMaxTextLength();
}
private Collection<String> getEntitiesForHints(Collection<String> hints) {
@@ -278,11 +277,11 @@
// Use the default if there is no hint, or conflicting ones.
final boolean useDefault = editable == notEditable;
if (useDefault) {
- return getSettings().getEntityListDefault();
+ return mSettings.getEntityListDefault();
} else if (editable) {
- return getSettings().getEntityListEditable();
+ return mSettings.getEntityListEditable();
} else { // notEditable
- return getSettings().getEntityListNotEditable();
+ return mSettings.getEntityListNotEditable();
}
}
@@ -298,16 +297,6 @@
}
}
- /** @hide */
- @Override
- public TextClassifierConstants getSettings() {
- if (mSettings == null) {
- mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
- mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
- }
- return mSettings;
- }
-
private TextClassifierImplNative getNative(LocaleList localeList)
throws FileNotFoundException {
synchronized (mLock) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 84c000a..ed122a6 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -39,7 +39,7 @@
// "file:///android_res/drawable/bar.png". Use "drawable" to refer to
// "drawable-hdpi" directory as well.
static final String RESOURCE_BASE = "file:///android_res/";
- static final String FILE_BASE = "file://";
+ static final String FILE_BASE = "file:";
static final String PROXY_BASE = "file:///cookieless_proxy/";
static final String CONTENT_BASE = "content:";
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a8f6b03..fadc3dc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1683,7 +1683,7 @@
* @param callback will be called on the UI thread with {@code true} if initialization is
* successful, {@code false} otherwise.
*/
- public static void startSafeBrowsing(Context context,
+ public static void startSafeBrowsing(@NonNull Context context,
@Nullable ValueCallback<Boolean> callback) {
getFactory().getStatics().initSafeBrowsing(context, callback);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2e7b2fd..02f35ca 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -107,6 +107,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextLinks;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView.Drawables;
@@ -4024,7 +4025,7 @@
private void updateAssistMenuItems(Menu menu) {
clearAssistMenuItems(menu);
- if (!mTextView.isDeviceProvisioned()) {
+ if (!shouldEnableAssistMenuItems()) {
return;
}
final TextClassification textClassification =
@@ -4097,7 +4098,7 @@
final TextClassification textClassification =
getSelectionActionModeHelper().getTextClassification();
- if (!mTextView.isDeviceProvisioned() || textClassification == null) {
+ if (!shouldEnableAssistMenuItems() || textClassification == null) {
// No textClassification result to handle the click. Eat the click.
return true;
}
@@ -4118,6 +4119,12 @@
return true;
}
+ private boolean shouldEnableAssistMenuItems() {
+ return mTextView.isDeviceProvisioned()
+ && TextClassificationManager.getSettings(mTextView.getContext())
+ .isSmartTextShareEnabled();
+ }
+
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
getSelectionActionModeHelper().onSelectionAction(item.getItemId());
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6ab09d6..12ab0ee 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -65,12 +67,10 @@
private static final String LOG_TAG = "SelectActionModeHelper";
- // TODO: Make this a configurable flag.
- private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
-
private final Editor mEditor;
private final TextView mTextView;
private final TextClassificationHelper mTextClassificationHelper;
+ private final TextClassificationConstants mTextClassificationSettings;
private TextClassification mTextClassification;
private AsyncTask mTextClassificationAsyncTask;
@@ -84,6 +84,7 @@
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
mTextView = mEditor.getTextView();
+ mTextClassificationSettings = TextClassificationManager.getSettings(mTextView.getContext());
mTextClassificationHelper = new TextClassificationHelper(
mTextView.getContext(),
mTextView.getTextClassifier(),
@@ -91,7 +92,7 @@
0, 1, mTextView.getTextLocales());
mSelectionTracker = new SelectionTracker(mTextView);
- if (SMART_SELECT_ANIMATION_ENABLED) {
+ if (mTextClassificationSettings.isSmartSelectionAnimationEnabled()) {
mSmartSelectSprite = new SmartSelectSprite(mTextView.getContext(),
editor.getTextView().mHighlightColor, mTextView::invalidate);
} else {
@@ -104,9 +105,7 @@
*/
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
- adjustSelection &= !mTextView.isTextEditable()
- || mTextView.getTextClassifier().getSettings()
- .isSuggestSelectionEnabledForEditableText();
+ adjustSelection &= mTextClassificationSettings.isSmartSelectionEnabled();
mSelectionTracker.onOriginalSelection(
getText(mTextView),
@@ -249,7 +248,7 @@
|| mTextView.isTextEditable()
|| actionMode == Editor.TextActionMode.TEXT_LINK)) {
// Do not change the selection if TextClassifier should be dark launched.
- if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
+ if (!mTextClassificationSettings.isModelDarkLaunchEnabled()) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextView.invalidate();
}
@@ -450,7 +449,6 @@
selectionEnd = mTextView.getSelectionEnd();
}
mTextClassificationHelper.init(
- mTextView.getContext(),
mTextView.getTextClassifier(),
getText(mTextView),
selectionStart, selectionEnd,
@@ -882,7 +880,8 @@
private static final int TRIM_DELTA = 120; // characters
- private Context mContext;
+ private final Context mContext;
+ private final boolean mDarkLaunchEnabled;
private TextClassifier mTextClassifier;
/** The original TextView text. **/
@@ -917,13 +916,15 @@
TextClassificationHelper(Context context, TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- init(context, textClassifier, text, selectionStart, selectionEnd, locales);
+ init(textClassifier, text, selectionStart, selectionEnd, locales);
+ mContext = Preconditions.checkNotNull(context);
+ mDarkLaunchEnabled = TextClassificationManager.getSettings(mContext)
+ .isModelDarkLaunchEnabled();
}
@UiThread
- public void init(Context context, TextClassifier textClassifier,
- CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- mContext = Preconditions.checkNotNull(context);
+ public void init(TextClassifier textClassifier, CharSequence text,
+ int selectionStart, int selectionEnd, LocaleList locales) {
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
mLastClassificationText = null; // invalidate.
@@ -956,7 +957,7 @@
mSelectionOptions.getDefaultLocales());
}
// Do not classify new selection boundaries if TextClassifier should be dark launched.
- if (!mTextClassifier.getSettings().isDarkLaunch()) {
+ if (!mDarkLaunchEnabled) {
mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
mSelectionEnd = Math.min(
mText.length(), selection.getSelectionEndIndex() + mTrimStart);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 50e6393..b482d47 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2025,7 +2025,7 @@
final boolean italic = (style & Typeface.ITALIC) != 0;
setTypeface(Typeface.create(typeface, weight, italic));
} else {
- setTypeface(Typeface.create(typeface, style));
+ setTypeface(typeface, style);
}
}
@@ -2111,7 +2111,7 @@
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
- public void setTypeface(Typeface tf, int style) {
+ public void setTypeface(@Nullable Typeface tf, @Typeface.Style int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
@@ -3896,7 +3896,7 @@
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
- public void setTypeface(Typeface tf) {
+ public void setTypeface(@Nullable Typeface tf) {
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index fabda4a..2505ea5 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -26,7 +26,8 @@
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
- int startOperation(IBinder token, int code, int uid, String packageName);
+ int startOperation(IBinder token, int code, int uid, String packageName,
+ boolean startIfModeDefault);
void finishOperation(IBinder token, int code, int uid, String packageName);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 5457c1d..5b6291e 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -113,19 +113,20 @@
public double bluetoothPowerMah;
public enum DrainType {
- IDLE,
- CELL,
- PHONE,
- WIFI,
- BLUETOOTH,
- FLASHLIGHT,
- SCREEN,
+ AMBIENT_DISPLAY,
APP,
- USER,
- UNACCOUNTED,
- OVERCOUNTED,
+ BLUETOOTH,
CAMERA,
- MEMORY
+ CELL,
+ FLASHLIGHT,
+ IDLE,
+ MEMORY,
+ OVERCOUNTED,
+ PHONE,
+ SCREEN,
+ UNACCOUNTED,
+ USER,
+ WIFI,
}
public BatterySipper(DrainType drainType, Uid uid, double value) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 5a59e70..a76cf0a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -643,6 +643,21 @@
}
}
+ /**
+ * Ambient display power is the additional power the screen takes while in ambient display/
+ * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should
+ * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing
+ * {@link #removeHiddenBatterySippers(List)}.
+ */
+ private void addAmbientDisplayUsage() {
+ long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType);
+ double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)
+ * ambientDisplayMs / (60 * 60 * 1000);
+ if (power > 0) {
+ addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power);
+ }
+ }
+
private void addRadioUsage() {
BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
@@ -741,6 +756,7 @@
addUserUsage();
addPhoneUsage();
addScreenUsage();
+ addAmbientDisplayUsage();
addWiFiUsage();
addBluetoothUsage();
addMemoryUsage();
@@ -841,12 +857,13 @@
final BatterySipper sipper = sippers.get(i);
sipper.shouldHide = shouldHideSipper(sipper);
if (sipper.shouldHide) {
- if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
- && sipper.drainType != BatterySipper.DrainType.SCREEN
- && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
- && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
- && sipper.drainType != BatterySipper.DrainType.WIFI
- && sipper.drainType != BatterySipper.DrainType.IDLE) {
+ if (sipper.drainType != DrainType.OVERCOUNTED
+ && sipper.drainType != DrainType.SCREEN
+ && sipper.drainType != DrainType.AMBIENT_DISPLAY
+ && sipper.drainType != DrainType.UNACCOUNTED
+ && sipper.drainType != DrainType.BLUETOOTH
+ && sipper.drainType != DrainType.WIFI
+ && sipper.drainType != DrainType.IDLE) {
// Don't add it if it is overcounted, unaccounted or screen
proportionalSmearPowerMah += sipper.totalPowerMah;
}
@@ -893,13 +910,14 @@
* Check whether we should hide the battery sipper.
*/
public boolean shouldHideSipper(BatterySipper sipper) {
- final BatterySipper.DrainType drainType = sipper.drainType;
+ final DrainType drainType = sipper.drainType;
- return drainType == BatterySipper.DrainType.IDLE
- || drainType == BatterySipper.DrainType.CELL
- || drainType == BatterySipper.DrainType.SCREEN
- || drainType == BatterySipper.DrainType.UNACCOUNTED
- || drainType == BatterySipper.DrainType.OVERCOUNTED
+ return drainType == DrainType.IDLE
+ || drainType == DrainType.CELL
+ || drainType == DrainType.SCREEN
+ || drainType == DrainType.AMBIENT_DISPLAY
+ || drainType == DrainType.UNACCOUNTED
+ || drainType == DrainType.OVERCOUNTED
|| isTypeService(sipper)
|| isTypeSystem(sipper);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8ee31f7..242f422 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -11597,7 +11597,7 @@
* time at the highest power level.
* @param activityInfo
*/
- private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
if (activityInfo == null) {
return;
}
@@ -13348,7 +13348,7 @@
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
- private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
+ private static final boolean DEFAULT_READ_BINARY_CPU_TIME = true;
private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
index 4d56905..396deb4 100644
--- a/core/java/com/android/internal/os/KernelCpuProcReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcReader.java
@@ -22,10 +22,12 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
@@ -124,6 +126,11 @@
}
fc.position(0);
}
+ } catch (NoSuchFileException | FileNotFoundException e) {
+ // Happens when the kernel does not provide this file. Not a big issue. Just log it.
+ mErrors++;
+ Slog.w(TAG, "File not exist: " + mProc);
+ return null;
} catch (IOException e) {
mErrors++;
Slog.e(TAG, "Error reading: " + mProc, e);
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index f4436d3..747d633 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -104,7 +104,7 @@
public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE =
"modem.controller.voltage";
- /**
+ /**
* Power consumption when GPS is on.
*/
public static final String POWER_GPS_ON = "gps.on";
@@ -136,6 +136,10 @@
@Deprecated
public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
+ /**
+ * Power consumption when screen is in doze/ambient/always-on mode, including backlight power.
+ */
+ public static final String POWER_AMBIENT_DISPLAY = "ambient.on";
/**
* Power consumption when screen is on, not including the backlight power.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 50c9d6c..528888f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2464,6 +2464,15 @@
decor.setSystemUiVisibility(
decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
+ if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
+ int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
+ if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
+ throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "
+ + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));
+ }
+ params.layoutInDisplayCutoutMode = mode;
+ }
if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.HONEYCOMB) {
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 67dc81a..48485e0 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -37,6 +37,7 @@
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
+import android.view.ViewConfiguration;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -753,8 +754,7 @@
private void setShortcutsVisibleInner(boolean shortcutsVisible) {
mShortcutsVisible = shortcutsVisible
&& mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
- && mResources.getBoolean(
- com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent);
+ && ViewConfiguration.get(mContext).shouldShowMenuShortcutsWhenKeyboardPresent();
}
/**
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 7c9cf7a..5a06f7f 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -65,6 +65,8 @@
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
// convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
+ void initRecoveryServiceWithSigFile(in String rootCertificateAlias,
+ in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile);
KeyChainSnapshot getKeyChainSnapshot();
byte[] generateAndStoreKey(String alias);
String generateKey(String alias);
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 5eecd9c..09adc82 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -23,16 +23,28 @@
YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
}
+struct ErrorMgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf jmp;
+};
+
+void error_exit(j_common_ptr cinfo) {
+ ErrorMgr* err = (ErrorMgr*) cinfo->err;
+ (*cinfo->err->output_message) (cinfo);
+ longjmp(err->jmp, 1);
+}
+
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
int height, int* offsets, int jpegQuality) {
jpeg_compress_struct cinfo;
- jpeg_error_mgr err;
+ ErrorMgr err;
skjpeg_destination_mgr sk_wstream(stream);
- cinfo.err = jpeg_std_error(&err);
- err.error_exit = skjpeg_error_exit;
- jmp_buf jmp;
- if (setjmp(jmp)) {
+ cinfo.err = jpeg_std_error(&err.pub);
+ err.pub.error_exit = error_exit;
+
+ if (setjmp(err.jmp)) {
+ jpeg_destroy_compress(&cinfo);
return false;
}
jpeg_create_compress(&cinfo);
@@ -47,6 +59,8 @@
jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
return true;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0ef5445..8ca5062 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -846,6 +846,14 @@
transaction->setOverrideScalingMode(ctrl, scalingMode);
}
+static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz,
+ jlong transactionObj,
+ jlong nativeObject) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->destroySurface(ctrl);
+}
+
static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
return javaObjectForIBinder(env, ctrl->getHandle());
@@ -997,6 +1005,8 @@
(void*)nativeSeverChildren } ,
{"nativeSetOverrideScalingMode", "(JJI)V",
(void*)nativeSetOverrideScalingMode },
+ {"nativeDestroy", "(JJ)V",
+ (void*)nativeDestroyInTransaction },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
{"nativeScreenshotToBuffer",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 13e0e4a..b614c89 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -984,6 +984,10 @@
Properties::enableRTAnimations = enable;
}
+static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) {
+ Properties::debuggingEnabled = enable;
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -1092,6 +1096,7 @@
{ "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText },
{ "nHackySetRTAnimationsEnabled", "(Z)V",
(void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
+ { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
};
static JavaVM* mJvm = nullptr;
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index 834ecde..74b47d2 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -35,9 +35,9 @@
optional uint32 mnc = 3;
repeated LocaleProto locales = 4;
optional uint32 screen_layout = 5;
- optional uint32 hdr_color_mode = 6;
- optional uint32 wide_color_gamut = 7;
- optional uint32 touchscreen = 8;
+ optional uint32 color_mode = 6;
+ optional uint32 touchscreen = 7;
+ optional uint32 keyboard = 8;
optional uint32 keyboard_hidden = 9;
optional uint32 hard_keyboard_hidden = 10;
optional uint32 navigation = 11;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index be15597..7326829 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -214,37 +214,37 @@
(section).args = "procstats --proto"
];
- optional com.android.server.am.proto.ActivityStackSupervisorProto activities = 3012 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto activities = 3012 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto activities"
];
- optional com.android.server.am.proto.BroadcastProto broadcasts = 3013 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto broadcasts = 3013 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto broadcasts"
];
- optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpServicesProto amservices = 3014 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto service"
];
- optional com.android.server.am.proto.ProcessesProto amprocesses = 3015 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto amprocesses = 3015 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto processes"
];
- optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
+ optional com.android.server.AlarmManagerServiceDumpProto alarm = 3016 [
(section).type = SECTION_DUMPSYS,
(section).args = "alarm --proto"
];
- optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [
+ optional com.android.server.wm.proto.WindowManagerServiceDumpProto window = 3017 [
(section).type = SECTION_DUMPSYS,
(section).args = "window --proto"
];
- optional com.android.server.am.proto.MemInfoProto meminfo = 3018 [
+ optional com.android.server.am.proto.MemInfoDumpProto meminfo = 3018 [
(section).type = SECTION_DUMPSYS,
(section).args = "meminfo -a --proto"
];
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 5042ede..3b9150f 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -40,16 +40,22 @@
message ActivityManagerServiceProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional ActivityStackSupervisorProto activities = 1;
+ optional ActivityManagerServiceDumpActivitiesProto activities = 1;
- optional BroadcastProto broadcasts = 2;
+ optional ActivityManagerServiceDumpBroadcastsProto broadcasts = 2;
- optional ActiveServicesProto services = 3;
+ optional ActivityManagerServiceDumpServicesProto services = 3;
- optional ProcessesProto processes = 4;
+ optional ActivityManagerServiceDumpProcessesProto processes = 4;
}
// "dumpsys activity --proto activities"
+message ActivityManagerServiceDumpActivitiesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ActivityStackSupervisorProto activity_stack_supervisor = 1;
+}
+
message ActivityStackSupervisorProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -121,7 +127,7 @@
}
// "dumpsys activity --proto broadcasts"
-message BroadcastProto {
+message ActivityManagerServiceDumpBroadcastsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated ReceiverListProto receiver_list = 1;
@@ -202,7 +208,7 @@
repeated BroadcastSummary historical_broadcasts_summary = 6;
}
-message MemInfoProto {
+message MemInfoDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 uptime_duration_ms = 1;
@@ -406,6 +412,12 @@
}
// "dumpsys activity --proto service"
+message ActivityManagerServiceDumpServicesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ActiveServicesProto active_services = 1;
+}
+
message ActiveServicesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -585,7 +597,7 @@
}
// TODO: "dumpsys activity --proto processes"
-message ProcessesProto {
+message ActivityManagerServiceDumpProcessesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated ProcessRecordProto procs = 1;
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index b288c11..53e3ba9 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -28,7 +28,7 @@
option java_multiple_files = true;
// next ID: 43
-message AlarmManagerServiceProto {
+message AlarmManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 current_time = 1;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c11058a..9598f24 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -32,7 +32,7 @@
option java_multiple_files = true;
-message WindowManagerServiceProto {
+message WindowManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional WindowManagerPolicyProto policy = 1;
@@ -295,7 +295,6 @@
optional bool animating_exit = 14;
repeated WindowStateProto child_windows = 15;
optional .android.graphics.RectProto surface_position = 16;
- optional .android.graphics.RectProto shown_position = 17;
optional int32 requested_width = 18;
optional int32 requested_height = 19;
optional int32 view_visibility = 20;
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
index d96953e..96a90bf 100644
--- a/core/proto/android/server/windowmanagertrace.proto
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -48,5 +48,5 @@
/* where the trace originated */
optional string where = 2;
- optional WindowManagerServiceProto window_manager_service = 3;
+ optional WindowManagerServiceDumpProto window_manager_service = 3;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a7178a0..5e12e7e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2978,7 +2978,18 @@
settings app. This permission cannot be granted to third-party apps.
<p>Protection level: signature
-->
- <permission android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS"
+ <permission
+ android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide
+ Allows an application to change the status of a persistable URI permission granted
+ to another application.
+ <p>This permission should <em>only</em> be requested by the platform
+ settings app. This permission cannot be granted to third-party apps.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.FORCE_PERSISTABLE_URI_PERMISSIONS"
android:protectionLevel="signature" />
<!-- @SystemApi Old permission for deleting an app's cache files, no longer used,
diff --git a/core/res/res/drawable/ic_alert_window_layer.xml b/core/res/res/drawable/ic_alert_window_layer.xml
new file mode 100644
index 0000000..15931b8
--- /dev/null
+++ b/core/res/res/drawable/ic_alert_window_layer.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_camera.xml b/core/res/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..2921a68
--- /dev/null
+++ b/core/res/res/drawable/ic_camera.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_mic.xml b/core/res/res/drawable/ic_mic.xml
new file mode 100644
index 0000000..3212330
--- /dev/null
+++ b/core/res/res/drawable/ic_mic.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml
index c1795640..2f94d0c 100644
--- a/core/res/res/drawable/progress_horizontal_material.xml
+++ b/core/res/res/drawable/progress_horizontal_material.xml
@@ -19,6 +19,7 @@
android:gravity="center_vertical|fill_horizontal">
<shape android:shape="rectangle"
android:tint="?attr/colorControlNormal">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -28,6 +29,7 @@
<scale android:scaleWidth="100%">
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -38,6 +40,7 @@
<scale android:scaleWidth="100%">
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white" />
</shape>
diff --git a/core/res/res/drawable/seekbar_track_material.xml b/core/res/res/drawable/seekbar_track_material.xml
index e88a73f..62ef136 100644
--- a/core/res/res/drawable/seekbar_track_material.xml
+++ b/core/res/res/drawable/seekbar_track_material.xml
@@ -19,6 +19,7 @@
android:gravity="center_vertical|fill_horizontal">
<shape android:shape="rectangle"
android:tint="@color/control_nodisable_material">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_background_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -32,6 +33,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_progress_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -48,6 +50,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_progress_height_material" />
<solid android:color="@color/white" />
</shape>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 20bdf3f..c03cf51 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
+<!-- extends ViewGroup -->
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@style/Theme.Material.Notification"
@@ -126,5 +126,42 @@
android:visibility="gone"
android:contentDescription="@string/notification_work_profile_content_description"
/>
+
+ <LinearLayout
+ android:id="@+id/app_ops"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal" >
+ <ImageButton
+ android:id="@+id/camera"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_camera"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="6dp"
+ android:visibility="gone"
+ />
+ <ImageButton
+ android:id="@+id/mic"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_mic"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="4dp"
+ android:visibility="gone"
+ />
+ <ImageButton
+ android:id="@+id/overlay"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_alert_window_layer"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="4dp"
+ android:visibility="gone"
+ />
+ </LinearLayout>
</NotificationHeaderView>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 4f0c0fb..265eaaf 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -27,6 +27,9 @@
<dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
<dimen name="preference_widget_width">72dp</dimen>
+ <!-- Height of the status bar -->
+ <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
+
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 22ab9c9..ffabdab 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1019,6 +1019,9 @@
<!-- Corner radius of buttons. -->
<attr name="buttonCornerRadius" format="dimension" />
+ <!-- Corner radius of progress bars. -->
+ <attr name="progressBarCornerRadius" format="dimension" />
+
<!-- Style for the search query widget. -->
<attr name="searchViewStyle" format="reference" />
@@ -2113,6 +2116,45 @@
Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} on
the decor view. -->
<attr name="windowLightNavigationBar" format="boolean" />
+
+ <!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
+ <p>
+ Defaults to {@code default}.
+
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ @see android.view.DisplayCutout
+ @see android.R.attr#layoutInDisplayCutoutMode -->
+ <attr name="windowLayoutInDisplayCutoutMode">
+ <!-- The window is allowed to extend into the {@code DisplayCutout} area, only if the
+ {@code DisplayCutout} is fully contained within the status bar. Otherwise, the window is
+ laid out such that it does not overlap with the {@code DisplayCutout} area.
+
+ @see android.view.DisplayCutout
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ -->
+ <enum name="default" value="0" />
+ <!-- The window is always allowed to extend into the {@code DisplayCutout} area,
+ even if fullscreen or in landscape.
+ <p>
+ The window must make sure that no important content overlaps with the
+ {@link DisplayCutout}.
+
+ @see android.view.DisplayCutout
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ -->
+ <enum name="always" value="1" />
+ <!-- The window is never allowed to overlap with the DisplayCutout area.
+ <p>
+ This should be used with windows that transiently set {@code SYSTEM_UI_FLAG_FULLSCREEN}
+ to avoid a relayout of the window when the flag is set or cleared.
+
+ @see android.view.DisplayCutout
+ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ -->
+ <enum name="never" value="2" />
+ </attr>
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cfb5784..c4fa190 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2348,6 +2348,16 @@
<attr name="logo" />
<attr name="priority" />
<attr name="autoVerify" />
+ <!-- Within an application, multiple intent filters may match a particular
+ intent. This allows the app author to specify the order filters should
+ be considered. We don't want to use priority because that is global
+ across applications.
+ <p>Only use if you really need to forcibly set the order in which
+ filters are evaluated. It is preferred to target an activity with a
+ directed intent instead.
+ <p>The value is a single integer, with higher numbers considered to
+ be better. If not specified, the default order is 0. -->
+ <attr name="order" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d6f3463..375627d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2163,6 +2163,9 @@
<!-- Whether UI for multi user should be shown -->
<bool name="config_enableMultiUserUI">false</bool>
+ <!-- Whether the new Auto Selection Network UI should be shown -->
+ <bool name="config_enableNewAutoSelectNetworkUI">false</bool>
+
<!-- If true, then we do not ask user for permission for apps to connect to USB devices.
Do not set this to true for production devices. Doing so will cause you to fail CTS. -->
<bool name="config_disableUsbPermissionDialogs">false</bool>
@@ -3331,6 +3334,8 @@
<dimen name="config_dialogCornerRadius">2dp</dimen>
<!-- Corner radius of system buttons -->
<dimen name="config_buttonCornerRadius">@dimen/control_corner_material</dimen>
+ <!-- Corner radius of system progress bars -->
+ <dimen name="config_progressBarCornerRadius">@dimen/progress_bar_corner_material</dimen>
<!-- Controls whether system buttons use all caps for text -->
<bool name="config_buttonTextAllCaps">true</bool>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2918260..7ff96fa 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -32,7 +32,11 @@
<dimen name="toast_y_offset">24dp</dimen>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">24dp</dimen>
+ <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
+ <!-- Height of the status bar in portrait -->
+ <dimen name="status_bar_height_portrait">24dp</dimen>
+ <!-- Height of the status bar in landscape -->
+ <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index e3fdcec..210f30e 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -135,6 +135,7 @@
<dimen name="seekbar_track_progress_height_material">2dp</dimen>
<dimen name="progress_bar_height_material">4dp</dimen>
+ <dimen name="progress_bar_corner_material">0dp</dimen>
<!-- Material time picker dimensions. -->
<!-- Text size for the time picker header HH:MM label. This value is large
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c4006b3..7d5d1ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2873,6 +2873,7 @@
<!-- @hide @SystemApi -->
<public name="userRestriction" />
<public name="textFontWeight" />
+ <public name="windowLayoutInDisplayCutoutMode" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c3ae5fa..f38dcea 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3320,8 +3320,7 @@
[CHAR LIMIT=NONE] -->
<string name="alert_windows_notification_message">If you don’t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string>
<!-- Notification action to turn-off app displaying on-top of other apps. [CHAR LIMIT=20] -->
- <string name="alert_windows_notification_turn_off_action">TURN OFF</string>
-
+ <string name="alert_windows_notification_turn_off_action">Turn off</string>
<!-- External media notification strings -->
<skip />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1babd70..8d67957 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -218,6 +218,10 @@
<java-symbol type="id" name="selection_end_handle" />
<java-symbol type="id" name="insertion_handle" />
<java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
+ <java-symbol type="id" name="camera" />
+ <java-symbol type="id" name="mic" />
+ <java-symbol type="id" name="overlay" />
+ <java-symbol type="id" name="app_ops" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -315,6 +319,7 @@
<java-symbol type="bool" name="config_wifi_softap_acs_supported" />
<java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
+ <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
@@ -1389,6 +1394,9 @@
<java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
<java-symbol type="drawable" name="autofilled_highlight"/>
+ <java-symbol type="drawable" name="ic_camera" />
+ <java-symbol type="drawable" name="ic_mic" />
+ <java-symbol type="drawable" name="ic_alert_window_layer" />
<java-symbol type="drawable" name="ic_account_circle" />
<java-symbol type="color" name="user_icon_1" />
@@ -3238,6 +3246,9 @@
<java-symbol type="string" name="keyguard_accessibility_sim_puk_unlock" />
<java-symbol type="string" name="keyguard_accessibility_password_unlock" />
+ <java-symbol type="dimen" name="status_bar_height_portrait" />
+ <java-symbol type="dimen" name="status_bar_height_landscape" />
+
<java-symbol type="string" name="global_action_logout" />
<java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="messaging_user" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 39310a8..cb11d8d 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -126,6 +126,7 @@
<item name="progressBarStyleInverse">@style/Widget.DeviceDefault.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.RatingBar.Indicator</item>
@@ -227,6 +228,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -247,6 +251,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -269,6 +276,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -290,6 +300,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -319,6 +332,9 @@
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -339,6 +355,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -358,6 +377,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -378,6 +400,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -414,6 +439,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -435,6 +463,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -454,6 +485,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -475,6 +509,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -495,6 +532,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -515,6 +555,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -535,6 +578,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -555,6 +601,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -575,6 +624,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
@@ -593,6 +645,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -611,6 +666,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -689,6 +747,7 @@
<item name="progressBarStyleInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.Light.RatingBar.Indicator</item>
@@ -785,6 +844,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -804,6 +866,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -824,6 +889,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -846,6 +914,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -867,6 +938,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -894,6 +968,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -914,6 +991,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -933,6 +1013,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -953,6 +1036,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -999,6 +1085,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -1020,6 +1109,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -1039,6 +1131,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -1060,6 +1155,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -1080,6 +1178,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -1098,6 +1199,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -1116,6 +1220,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -1147,6 +1254,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -1166,6 +1276,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -1175,6 +1288,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -1195,6 +1311,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -1214,6 +1333,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -1233,6 +1355,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -1252,6 +1377,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -1278,6 +1406,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for the default system theme. -->
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 6ae0ef3..76d9ea6 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -259,6 +259,7 @@
<item name="progressBarStyleInverse">@style/Widget.Material.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.Material.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.Material.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/progress_bar_corner_material</item>
<item name="seekBarStyle">@style/Widget.Material.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.Material.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.Material.RatingBar.Indicator</item>
@@ -631,6 +632,7 @@
<item name="progressBarStyleInverse">@style/Widget.Material.Light.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.Material.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.Material.Light.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/progress_bar_corner_material</item>
<item name="seekBarStyle">@style/Widget.Material.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.Material.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.Material.Light.RatingBar.Indicator</item>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index d80c697..bd0958d 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -28,8 +28,9 @@
significantly, so should be measured on the shipping platform
with a power meter. -->
<item name="none">0</item>
- <item name="screen.on">0.1</item> <!-- ~200mA -->
- <item name="screen.full">0.1</item> <!-- ~300mA -->
+ <item name="ambient.on">0.1</item> <!-- ~100mA -->
+ <item name="screen.on">0.1</item> <!-- ~100mA -->
+ <item name="screen.full">0.1</item> <!-- ~100mA -->
<item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
<item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
<item name="wifi.on">0.1</item> <!-- ~3mA -->
diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml
index cdb7134..eb34732 100644
--- a/core/res/res/xml/power_profile_test.xml
+++ b/core/res/res/xml/power_profile_test.xml
@@ -80,6 +80,8 @@
<value>60</value> <!-- 3000 MHz CPU speed -->
</array>
+ <!-- Power used by display unit in ambient display mode, including back lighting-->
+ <item name="ambient.on">0.5</item>
<!-- Additional power used when screen is turned on at minimum brightness -->
<item name="screen.on">100</item>
<!-- Additional power used when screen is at maximum brightness, compared to
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index 7a90197..ef3a481 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -19,4 +19,16 @@
<item name="android:taskToBackEnterAnimation">@null</item>
<item name="android:taskToBackExitAnimation">@null</item>
</style>
+
+ <style name="LayoutInDisplayCutoutModeUnset">
+ </style>
+ <style name="LayoutInDisplayCutoutModeDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+ <style name="LayoutInDisplayCutoutModeAlways">
+ <item name="android:windowLayoutInDisplayCutoutMode">always</item>
+ </style>
+ <style name="LayoutInDisplayCutoutModeNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index e575650..3eefc36 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -24,6 +24,9 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -48,6 +51,10 @@
import org.mockito.InOrder;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
/** Test {@link TransactionExecutor} logic. */
@RunWith(AndroidJUnit4.class)
@@ -56,6 +63,7 @@
public class TransactionExecutorTests {
private TransactionExecutor mExecutor;
+ private TransactionExecutorHelper mExecutorHelper;
private ClientTransactionHandler mTransactionHandler;
private ActivityClientRecord mClientRecord;
@@ -67,6 +75,7 @@
when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
mExecutor = spy(new TransactionExecutor(mTransactionHandler));
+ mExecutorHelper = new TransactionExecutorHelper();
}
@Test
@@ -166,10 +175,42 @@
pathExcludeLast(ON_DESTROY));
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleUndefinedStartState() {
+ mClientRecord.setState(UNDEFINED);
+ path(ON_CREATE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleUndefinedFinishState() {
+ mClientRecord.setState(PRE_ON_CREATE);
+ path(UNDEFINED);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidPreOnCreateFinishState() {
+ mClientRecord.setState(ON_CREATE);
+ path(PRE_ON_CREATE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidOnRestartStartState() {
+ mClientRecord.setState(ON_RESTART);
+ path(ON_RESUME);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidOnRestartFinishState() {
+ mClientRecord.setState(ON_CREATE);
+ path(ON_RESTART);
+ }
+
@Test
public void testTransactionResolution() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+ when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+ when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
IBinder token = mock(IBinder.class);
@@ -189,7 +230,7 @@
}
@Test
- public void testRequiredStateResolution() {
+ public void testActivityResultRequiredStateResolution() {
ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>());
IBinder token = mock(IBinder.class);
@@ -197,20 +238,161 @@
token /* activityToken */);
transaction.addCallback(activityResultItem);
+ // Verify resolution that should get to onPause
+ mClientRecord.setState(ON_RESUME);
mExecutor.executeCallbacks(transaction);
-
verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
+
+ // Verify resolution that should get to onStart
+ mClientRecord.setState(ON_STOP);
+ mExecutor.executeCallbacks(transaction);
+ verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START));
+ }
+
+ @Test
+ public void testClosestStateResolutionForSameState() {
+ final int[] allStates = new int[] {
+ ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
+
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_RESUME);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_STOP);
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(PRE_ON_CREATE);
+ assertEquals(PRE_ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ new int[] {PRE_ON_CREATE}));
+ }
+
+ @Test
+ public void testClosestStateResolution() {
+ mClientRecord.setState(PRE_ON_CREATE);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_STOP, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_DESTROY})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnCreate() {
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnStart() {
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnResume() {
+ mClientRecord.setState(ON_RESUME);
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnPause() {
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnStop() {
+ mClientRecord.setState(ON_STOP);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnDestroy() {
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP})));
+ }
+
+ @Test
+ public void testClosestStateResolutionToUndefined() {
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(UNDEFINED,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, UNDEFINED));
+ }
+
+ @Test
+ public void testClosestStateResolutionToOnResume() {
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_START,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_START,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_PAUSE,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ }
+
+ private static int[] shuffledArray(int[] inputArray) {
+ final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList());
+ Collections.shuffle(list);
+ return list.stream().mapToInt(Integer::intValue).toArray();
}
private int[] path(int finish) {
- mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
- false /* excludeLastState */);
- return mExecutor.getLifecycleSequence();
+ return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
+ false /* excludeLastState */).toArray();
}
private int[] pathExcludeLast(int finish) {
- mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
- true /* excludeLastState */);
- return mExecutor.getLifecycleSequence();
+ return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
+ true /* excludeLastState */).toArray();
}
}
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
new file mode 100644
index 0000000..8deccb7
--- /dev/null
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import static org.junit.Assert.fail;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BroadcastReceiverTests {
+
+ private static final int RECEIVER_LIMIT_PER_APP = 1000;
+ private static final class EmptyReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Empty
+ }
+ }
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testReceiverLimit() {
+ final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
+ try {
+ for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
+ mContext.registerReceiver(new EmptyReceiver(), mockFilter);
+ }
+ fail("No exception thrown when registering "
+ + (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
+ } catch (IllegalStateException ise) {
+ // Expected
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index f4d4c44..d6580d6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -314,6 +314,8 @@
Settings.Global.NTP_SERVER,
Settings.Global.NTP_TIMEOUT,
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
+ Settings.Global.OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
+ Settings.Global.OFF_BODY_RADIOS_OFF_DELAY_MS,
Settings.Global.OVERLAY_DISPLAY_DEVICES,
Settings.Global.PAC_CHANGE_DELAY,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
new file mode 100644
index 0000000..54007fb
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationConstantsTest {
+
+ @Test
+ public void testLoadFromString() {
+ final String s = "local_textclassifier_enabled=true,"
+ + "system_textclassifier_enabled=true,"
+ + "model_dark_launch_enabled=true,"
+ + "smart_selection_enabled=true,"
+ + "smart_text_share_enabled=true,"
+ + "smart_linkify_enabled=true,"
+ + "smart_select_animation_enabled=true,"
+ + "suggest_selection_max_range_length=10,"
+ + "classify_text_max_range_length=11,"
+ + "generate_links_max_text_length=12,"
+ + "generate_links_log_sample_rate=13";
+ final TextClassificationConstants constants =
+ TextClassificationConstants.loadFromString(s);
+ assertTrue("local_textclassifier_enabled",
+ constants.isLocalTextClassifierEnabled());
+ assertTrue("system_textclassifier_enabled",
+ constants.isSystemTextClassifierEnabled());
+ assertTrue("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+ assertTrue("smart_selection_enabled", constants.isSmartSelectionEnabled());
+ assertTrue("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+ assertTrue("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+ assertTrue("smart_select_animation_enabled", constants.isSmartSelectionAnimationEnabled());
+ assertEquals("suggest_selection_max_range_length",
+ 10, constants.getSuggestSelectionMaxRangeLength());
+ assertEquals("classify_text_max_range_length",
+ 11, constants.getClassifyTextMaxRangeLength());
+ assertEquals("generate_links_max_text_length",
+ 12, constants.getGenerateLinksMaxTextLength());
+ assertEquals("generate_links_log_sample_rate",
+ 13, constants.getGenerateLinksLogSampleRate());
+ }
+
+ @Test
+ public void testLoadFromString_differentValues() {
+ final String s = "local_textclassifier_enabled=false,"
+ + "system_textclassifier_enabled=false,"
+ + "model_dark_launch_enabled=false,"
+ + "smart_selection_enabled=false,"
+ + "smart_text_share_enabled=false,"
+ + "smart_linkify_enabled=false,"
+ + "smart_select_animation_enabled=false,"
+ + "suggest_selection_max_range_length=8,"
+ + "classify_text_max_range_length=7,"
+ + "generate_links_max_text_length=6,"
+ + "generate_links_log_sample_rate=5";
+ final TextClassificationConstants constants =
+ TextClassificationConstants.loadFromString(s);
+ assertFalse("local_textclassifier_enabled",
+ constants.isLocalTextClassifierEnabled());
+ assertFalse("system_textclassifier_enabled",
+ constants.isSystemTextClassifierEnabled());
+ assertFalse("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+ assertFalse("smart_selection_enabled", constants.isSmartSelectionEnabled());
+ assertFalse("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+ assertFalse("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+ assertFalse("smart_select_animation_enabled",
+ constants.isSmartSelectionAnimationEnabled());
+ assertEquals("suggest_selection_max_range_length",
+ 8, constants.getSuggestSelectionMaxRangeLength());
+ assertEquals("classify_text_max_range_length",
+ 7, constants.getClassifyTextMaxRangeLength());
+ assertEquals("generate_links_max_text_length",
+ 6, constants.getGenerateLinksMaxTextLength());
+ assertEquals("generate_links_log_sample_rate",
+ 5, constants.getGenerateLinksLogSampleRate());
+ }
+
+ @Test
+ public void testEntityListParsing() {
+ final TextClassificationConstants constants = TextClassificationConstants.loadFromString(
+ "entity_list_default=phone,"
+ + "entity_list_not_editable=address:flight,"
+ + "entity_list_editable=date:datetime");
+ assertEquals(1, constants.getEntityListDefault().size());
+ assertEquals("phone", constants.getEntityListDefault().get(0));
+ assertEquals(2, constants.getEntityListNotEditable().size());
+ assertEquals("address", constants.getEntityListNotEditable().get(0));
+ assertEquals("flight", constants.getEntityListNotEditable().get(1));
+ assertEquals(2, constants.getEntityListEditable().size());
+ assertEquals("date", constants.getEntityListEditable().get(0));
+ assertEquals("datetime", constants.getEntityListEditable().get(1));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 5407ce6..57db153 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -22,7 +22,9 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.content.Context;
import android.os.LocaleList;
+import android.service.textclassifier.TextClassifierService;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -44,6 +46,7 @@
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
private static final String NO_TYPE = null;
+ private Context mContext;
private TextClassificationManager mTcm;
private TextClassifier mClassifier;
private TextSelection.Options mSelectionOptions;
@@ -52,8 +55,8 @@
@Before
public void setup() {
- mTcm = InstrumentationRegistry.getTargetContext()
- .getSystemService(TextClassificationManager.class);
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTcm = mContext.getSystemService(TextClassificationManager.class);
mTcm.setTextClassifier(null);
mClassifier = mTcm.getTextClassifier();
mSelectionOptions = new TextSelection.Options().setDefaultLocales(LOCALES);
@@ -282,6 +285,18 @@
assertEquals(classifier, mTcm.getTextClassifier());
}
+ @Test
+ public void testGetLocalTextClassifier() {
+ assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl);
+ }
+
+ @Test
+ public void testGetSystemTextClassifier() {
+ assertTrue(
+ TextClassifierService.getServiceComponentName(mContext) == null
+ || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
+ }
+
private boolean isTextClassifierDisabled() {
return mClassifier == TextClassifier.NO_OP;
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
deleted file mode 100644
index 984eede..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static org.junit.Assert.assertEquals;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierConstantsTest {
-
- @Test
- public void testEntityListParsing() {
- final TextClassifierConstants constants = TextClassifierConstants.loadFromString(
- "entity_list_default=phone,"
- + "entity_list_not_editable=address:flight,"
- + "entity_list_editable=date:datetime");
- assertEquals(1, constants.getEntityListDefault().size());
- assertEquals("phone", constants.getEntityListDefault().get(0));
- assertEquals(2, constants.getEntityListNotEditable().size());
- assertEquals("address", constants.getEntityListNotEditable().get(0));
- assertEquals("flight", constants.getEntityListNotEditable().get(1));
- assertEquals(2, constants.getEntityListEditable().size());
- assertEquals("date", constants.getEntityListEditable().get(0));
- assertEquals("datetime", constants.getEntityListEditable().get(1));
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index eb7da9c..c7de99a 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -53,6 +53,7 @@
assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
assertEquals(3000.0, mProfile.getBatteryCapacity());
+ assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
}
}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
new file mode 100644
index 0000000..c8994dd
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link PhoneWindow}'s {@link ActionMode} related methods.
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit once monitored to be stable.")
+@RunWith(AndroidJUnit4.class)
+public final class PhoneWindowTest {
+
+ private PhoneWindow mPhoneWindow;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
+ @Test
+ public void layoutInDisplayCutoutMode_unset() throws Exception {
+ createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeUnset);
+ installDecor();
+
+ assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
+ is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT));
+ }
+
+ @Test
+ public void layoutInDisplayCutoutMode_default() throws Exception {
+ createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeDefault);
+ installDecor();
+
+ assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
+ is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT));
+ }
+
+ @Test
+ public void layoutInDisplayCutoutMode_always() throws Exception {
+ createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeAlways);
+ installDecor();
+
+ assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
+ is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS));
+ }
+
+ @Test
+ public void layoutInDisplayCutoutMode_never() throws Exception {
+ createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeNever);
+ installDecor();
+
+ assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
+ is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER));
+ }
+
+ private void createPhoneWindowWithTheme(int theme) {
+ mPhoneWindow = new PhoneWindow(new ContextThemeWrapper(mContext, theme));
+ }
+
+ private void installDecor() {
+ mPhoneWindow.getDecorView();
+ }
+}
\ No newline at end of file
diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java
index a9e180f..003678a 100644
--- a/graphics/java/android/graphics/EmbossMaskFilter.java
+++ b/graphics/java/android/graphics/EmbossMaskFilter.java
@@ -20,12 +20,15 @@
/**
* Create an emboss maskfilter
*
+ * @deprecated This subclass is not supported and should not be instantiated.
+ *
* @param direction array of 3 scalars [x, y, z] specifying the direction of the light source
* @param ambient 0...1 amount of ambient light
* @param specular coefficient for specular highlights (e.g. 8)
* @param blurRadius amount to blur before applying lighting (e.g. 3)
* @return the emboss maskfilter
*/
+ @Deprecated
public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) {
if (direction.length < 3) {
throw new ArrayIndexOutOfBoundsException();
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 1521547..5abd31a 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -621,16 +621,18 @@
/**
* Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
*
- * <p>The returned {@link Source} effectively takes ownership of the
- * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
- * this call.</p>
+ * <p>Decoding will start from {@link java.nio.ByteBuffer#position()}. The
+ * position of {@code buffer} will not be affected.</p>
*
- * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
- * position after decoding is undefined.
+ * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable}, and
+ * the encoded image is animated, the returned {@link AnimatedImageDrawable}
+ * will continue reading from the {@code buffer}, so its contents must not
+ * be modified, even after the {@code AnimatedImageDrawable} is returned.
+ * {@code buffer}'s contents should never be modified during decode.</p>
*/
@NonNull
public static Source createSource(@NonNull ByteBuffer buffer) {
- return new ByteBufferSource(buffer);
+ return new ByteBufferSource(buffer.slice());
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index e74dc6d..54358e3 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -201,10 +201,10 @@
* android:drawable="@drawable/vectordrawable" >
* <target
* android:name="rotationGroup"
- * android:animation="@anim/rotation" />
+ * android:animation="@animator/rotation" />
* <target
* android:name="v"
- * android:animation="@anim/path_morph" />
+ * android:animation="@animator/path_morph" />
* </animated-vector>
* </pre>
* </li>
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 749b7594..361fe0b 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -18,11 +18,14 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
-import android.content.res.ColorStateList;
+import android.annotation.IdRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -60,17 +63,40 @@
public final class Icon implements Parcelable {
private static final String TAG = "Icon";
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
+ * @see #getType
+ */
public static final int TYPE_BITMAP = 1;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithResource}.
+ * @see #getType
+ */
public static final int TYPE_RESOURCE = 2;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithData(byte[], int, int)}.
+ * @see #getType
+ */
public static final int TYPE_DATA = 3;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithContentUri}
+ * or {@link Icon#createWithFilePath(String)}.
+ * @see #getType
+ */
public static final int TYPE_URI = 4;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithAdaptiveBitmap}.
+ * @see #getType
+ */
public static final int TYPE_ADAPTIVE_BITMAP = 5;
+ /**
+ * @hide
+ */
+ @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+ public @interface IconType {
+ }
+
private static final int VERSION_STREAM_SERIALIZER = 1;
private final int mType;
@@ -99,14 +125,12 @@
private int mInt2;
/**
- * @return The type of image data held in this Icon. One of
- * {@link #TYPE_BITMAP},
- * {@link #TYPE_RESOURCE},
- * {@link #TYPE_DATA}, or
- * {@link #TYPE_URI}.
- * {@link #TYPE_ADAPTIVE_BITMAP}
- * @hide
+ * Gets the type of the icon provided.
+ * <p>
+ * Note that new types may be added later, so callers should guard against other
+ * types being returned.
*/
+ @IconType
public int getType() {
return mType;
}
@@ -179,9 +203,13 @@
}
/**
- * @return The package containing resources for this {@link #TYPE_RESOURCE} Icon.
- * @hide
+ * Gets the package used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_RESOURCE}.
+ * Note: This package may not be available if referenced in the future, and it is
+ * up to the caller to ensure safety if this package is re-used and/or persisted.
*/
+ @NonNull
public String getResPackage() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResPackage() on " + this);
@@ -190,9 +218,13 @@
}
/**
- * @return The resource ID for this {@link #TYPE_RESOURCE} Icon.
- * @hide
+ * Gets the resource used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_RESOURCE}.
+ * Note: This resource may not be available if the application changes at all, and it is
+ * up to the caller to ensure safety if this resource is re-used and/or persisted.
*/
+ @IdRes
public int getResId() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResId() on " + this);
@@ -212,9 +244,13 @@
}
/**
- * @return The {@link android.net.Uri} for this {@link #TYPE_URI} Icon.
- * @hide
+ * Gets the uri used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_URI}.
+ * Note: This uri may not be available in the future, and it is
+ * up to the caller to ensure safety if this uri is re-used and/or persisted.
*/
+ @NonNull
public Uri getUri() {
return Uri.parse(getUriString());
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 24d819e..35790b6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -6,6 +6,7 @@
//"hwui_bugreport_font_cache_usage",
//"hwui_compile_for_perf",
"hwui_pgo",
+ "hwui_lto",
],
cpp_std: "c++17",
@@ -126,6 +127,18 @@
},
}
+// Build hwui library with ThinLTO by default.
+cc_defaults {
+ name: "hwui_lto",
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
+}
+
// ------------------------
// library
// ------------------------
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 6cd283a..e9adbdc 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -62,6 +62,7 @@
bool Properties::enableRTAnimations = true;
bool Properties::runningInEmulator = false;
+bool Properties::debuggingEnabled = false;
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {
@@ -134,8 +135,7 @@
filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false);
- skpCaptureEnabled = property_get_bool("ro.debuggable", false) &&
- property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
+ skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 179b97b..3f44c21 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -268,6 +268,8 @@
static bool runningInEmulator;
+ ANDROID_API static bool debuggingEnabled;
+
private:
static ProfileType sProfileType;
static bool sDisableProfileBars;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 4be7a57..c6a9b55f 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -356,14 +356,21 @@
void RenderProxy::repackVectorDrawableAtlas() {
RenderThread& thread = RenderThread::getInstance();
thread.queue().post([&thread]() {
- thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(thread.getGrContext());
+ // The context may be null if trimMemory executed, but then the atlas was deleted too.
+ if (thread.getGrContext() != nullptr) {
+ thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(
+ thread.getGrContext());
+ }
});
}
void RenderProxy::releaseVDAtlasEntries() {
RenderThread& thread = RenderThread::getInstance();
thread.queue().post([&thread]() {
- thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries();
+ // The context may be null if trimMemory executed, but then the atlas was deleted too.
+ if (thread.getGrContext() != nullptr) {
+ thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries();
+ }
});
}
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 6dff07f..6d58a94 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -74,7 +74,7 @@
private List<HttpCookie> mUriCookies;
private Context mUriContext;
- private long mId = 0;
+ private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = LONG_MAX;
@@ -82,11 +82,11 @@
}
/**
- * Return the Id of data source.
- * @return the Id of data source
+ * Return the media Id of data source.
+ * @return the media Id of data source
*/
- public long getId() {
- return mId;
+ public String getMediaId() {
+ return mMediaId;
}
/**
@@ -222,7 +222,7 @@
private List<HttpCookie> mUriCookies;
private Context mUriContext;
- private long mId = 0;
+ private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = LONG_MAX;
@@ -248,7 +248,7 @@
mUriCookies = dsd.mUriCookies;
mUriContext = dsd.mUriContext;
- mId = dsd.mId;
+ mMediaId = dsd.mMediaId;
mStartPositionMs = dsd.mStartPositionMs;
mEndPositionMs = dsd.mEndPositionMs;
}
@@ -282,7 +282,7 @@
dsd.mUriCookies = mUriCookies;
dsd.mUriContext = mUriContext;
- dsd.mId = mId;
+ dsd.mMediaId = mMediaId;
dsd.mStartPositionMs = mStartPositionMs;
dsd.mEndPositionMs = mEndPositionMs;
@@ -290,13 +290,13 @@
}
/**
- * Sets the Id of this data source.
+ * Sets the media Id of this data source.
*
- * @param id the Id of this data source
+ * @param mediaId the media Id of this data source
* @return the same Builder instance.
*/
- public Builder setId(long id) {
- mId = id;
+ public Builder setMediaId(String mediaId) {
+ mMediaId = mediaId;
return this;
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 74e7c45..4af8850 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -181,6 +181,10 @@
oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
+ int addMixForPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb);
+
+ int removeMixForPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb);
+
int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb);
void setVolumePolicy(in VolumePolicy policy);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index fbe5561..6dd4f69 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -19,7 +19,9 @@
import java.nio.ByteBuffer;
import java.lang.AutoCloseable;
+import android.annotation.Nullable;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
/**
* <p>A single complete image buffer to use with a media source such as a
@@ -184,6 +186,23 @@
public abstract long getTimestamp();
/**
+ * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
+ * intended for GPU and/or hardware access.
+ * <p>
+ * The returned {@link android.hardware.HardwareBuffer HardwareBuffer} shall not be used
+ * after {@link Image#close Image.close()} has been called.
+ * </p>
+ * @return the HardwareBuffer associated with this Image or null if this Image doesn't support
+ * this feature (e.g. {@link android.media.ImageWriter ImageWriter} or
+ * {@link android.media.MediaCodec MediaCodec} don't).
+ */
+ @Nullable
+ public HardwareBuffer getHardwareBuffer() {
+ throwISEIfImageIsInvalid();
+ return null;
+ }
+
+ /**
* Set the timestamp associated with this frame.
* <p>
* The timestamp is measured in nanoseconds, and is normally monotonically
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 1019580..fb0de5c 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -876,6 +876,12 @@
}
@Override
+ public HardwareBuffer getHardwareBuffer() {
+ throwISEIfImageIsInvalid();
+ return nativeGetHardwareBuffer();
+ }
+
+ @Override
public void setTimestamp(long timestampNs) {
throwISEIfImageIsInvalid();
mTimestamp = timestampNs;
@@ -1017,6 +1023,7 @@
private synchronized native int nativeGetWidth();
private synchronized native int nativeGetHeight();
private synchronized native int nativeGetFormat(int readerFormat);
+ private synchronized native HardwareBuffer nativeGetHardwareBuffer();
}
private synchronized native void nativeInit(Object weakSelf, int w, int h,
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index c99a19b..38d5000 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -156,7 +156,7 @@
* @param updateTimeMs timestamp when the position information is sent from the session
* @param positionMs position in millis
*/
- public void onPositionUpdated(long updateTimeMs, long positionMs) { }
+ public void onPositionChanged(long updateTimeMs, long positionMs) { }
/**
* Called when playback speed is changed.
@@ -176,9 +176,9 @@
* Called when a error from
*
* @param errorCode error code
- * @param extra extra information
+ * @param extras extra information
*/
- public void onError(@ErrorCode int errorCode, int extra) { }
+ public void onError(@ErrorCode int errorCode, @Nullable Bundle extras) { }
/**
* Called when the player's current playing item is changed
@@ -599,7 +599,7 @@
}
/**
- * Get the lastly cached position from {@link ControllerCallback#onPositionUpdated(long, long)}.
+ * Get the lastly cached position from {@link ControllerCallback#onPositionChanged(long, long)}.
* <p>
* This returns the calculated value of the position, based on the difference between the
* update time and current time.
@@ -621,6 +621,13 @@
}
/**
+ * Set the playback speed.
+ */
+ public void setPlaybackSpeed(float speed) {
+ // TODO: implement this
+ }
+
+ /**
* Get the lastly cached buffered position from
* {@link ControllerCallback#onBufferedPositionChanged(long)}.
*
@@ -700,7 +707,7 @@
/**
* Removes the media item at index in the playlist.
*<p>
- * If index is same as the current index of the playlist, current playback
+ * If the item is the currently playing item of the playlist, current playback
* will be stopped and playback moves to next source in the list.
*/
@Override
diff --git a/media/java/android/media/MediaDescrambler.java b/media/java/android/media/MediaDescrambler.java
index 40c837b..99bd254 100644
--- a/media/java/android/media/MediaDescrambler.java
+++ b/media/java/android/media/MediaDescrambler.java
@@ -125,6 +125,38 @@
}
/**
+ * Scramble control value indicating that the samples are not scrambled.
+ * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+ */
+ public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0;
+
+ /**
+ * Scramble control value reserved and shouldn't be used currently.
+ * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+ */
+ public static final byte SCRAMBLE_CONTROL_RESERVED = 1;
+
+ /**
+ * Scramble control value indicating that the even key is used.
+ * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+ */
+ public static final byte SCRAMBLE_CONTROL_EVEN_KEY = 2;
+
+ /**
+ * Scramble control value indicating that the odd key is used.
+ * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+ */
+ public static final byte SCRAMBLE_CONTROL_ODD_KEY = 3;
+
+ /**
+ * Scramble flag for a hint indicating that the descrambling request is for
+ * retrieving the PES header info only.
+ *
+ * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+ */
+ public static final byte SCRAMBLE_FLAG_PES_HEADER = (1 << 0);
+
+ /**
* Descramble a ByteBuffer of data described by a
* {@link android.media.MediaCodec.CryptoInfo} structure.
*
@@ -133,7 +165,15 @@
* @param dstBuf ByteBuffer to hold the descrambled data, which starts at
* dstBuf.position().
* @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
- * describing the subsamples contained in src.
+ * describing the subsamples contained in srcBuf. The iv and mode fields in
+ * CryptoInfo are not used. key[0] contains the MPEG2TS scrambling control bits
+ * (as defined in ETSI TS 100 289 (2011): "Digital Video Broadcasting (DVB);
+ * Support for use of the DVB Scrambling Algorithm version 3 within digital
+ * broadcasting systems"), and the value must be one of {@link #SCRAMBLE_CONTROL_UNSCRAMBLED},
+ * {@link #SCRAMBLE_CONTROL_RESERVED}, {@link #SCRAMBLE_CONTROL_EVEN_KEY} or
+ * {@link #SCRAMBLE_CONTROL_ODD_KEY}. key[1] is a set of bit flags, with the
+ * only possible bit being {@link #SCRAMBLE_FLAG_PES_HEADER} currently.
+ * key[2~15] are not used.
*
* @return number of bytes that have been successfully descrambled, with negative
* values indicating errors.
@@ -169,6 +209,7 @@
try {
return native_descramble(
cryptoInfo.key[0],
+ cryptoInfo.key[1],
cryptoInfo.numSubSamples,
cryptoInfo.numBytesOfClearData,
cryptoInfo.numBytesOfEncryptedData,
@@ -204,7 +245,8 @@
private native final void native_setup(@NonNull IHwBinder decramblerBinder);
private native final void native_release();
private native final int native_descramble(
- byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
+ byte key, byte flags, int numSubSamples,
+ int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
@NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit,
ByteBuffer dstBuf, int dstOffset, int dstLimit) throws RemoteException;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 279e05f..e0c567b 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -628,7 +628,8 @@
}
/**
- * Open a new session with the MediaDrm object. A session ID is returned.
+ * Open a new session with the MediaDrm object. A session ID is returned.
+ * By default, sessions are opened at the native security level of the device.
*
* @throws NotProvisionedException if provisioning is needed
* @throws ResourceBusyException if required resources are in use
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index b7b75e4..f9eceab 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -57,22 +57,6 @@
private final MediaItem2Provider mProvider;
/**
- * Create a new media item.
- *
- * @param mediaId id of this item. It must be unique whithin this app
- * @param metadata metadata with the media id.
- * @param flags The flags for this item.
- * @hide
- */
- // TODO(jaewan): Remove this
- public MediaItem2(@NonNull Context context, @NonNull String mediaId,
- @NonNull DataSourceDesc dsd, @Nullable MediaMetadata2 metadata,
- @Flags int flags) {
- mProvider = ApiLoader.getProvider(context).createMediaItem2(
- context, this, mediaId, dsd, metadata, flags);
- }
-
- /**
* Create a new media item
* @hide
*/
@@ -159,13 +143,8 @@
/**
* Build {@link MediaItem2}
*/
- // TODO(jaewan): Move it to updatable
public static final class Builder {
- private Context mContext;
- private @Flags int mFlags;
- private String mMediaId;
- private MediaMetadata2 mMetadata;
- private DataSourceDesc mDataSourceDesc;
+ private final MediaItem2Provider.BuilderProvider mProvider;
/**
* Constructor for {@link Builder}
@@ -174,8 +153,8 @@
* @param flags
*/
public Builder(@NonNull Context context, @Flags int flags) {
- mContext = context;
- mFlags = flags;
+ mProvider = ApiLoader.getProvider(context).createMediaItem2Builder(
+ context, this, flags);
}
/**
@@ -192,8 +171,7 @@
* @return this instance for chaining
*/
public Builder setMediaId(@Nullable String mediaId) {
- mMediaId = mediaId;
- return this;
+ return mProvider.setMediaId_impl(mediaId);
}
/**
@@ -208,19 +186,17 @@
* @return this instance for chaining
*/
public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
- mMetadata = metadata;
- return this;
+ return mProvider.setMetadata_impl(metadata);
}
/**
- * Set the data source descriptor for this instance. {@code null} for unset.
+ * Set the data source descriptor for this instance. Should not be {@code null}.
*
* @param dataSourceDesc data source descriptor
* @return this instance for chaining
*/
- public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
- mDataSourceDesc = dataSourceDesc;
- return this;
+ public Builder setDataSourceDesc(@NonNull DataSourceDesc dataSourceDesc) {
+ return mProvider.setDataSourceDesc_impl(dataSourceDesc);
}
/**
@@ -229,13 +205,7 @@
* @return a new {@link MediaItem2}.
*/
public MediaItem2 build() {
- String id = (mMetadata != null)
- ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null;
- if (id == null) {
- // TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?)
- id = (mMediaId != null) ? mMediaId : toString();
- }
- return new MediaItem2(mContext, id, mDataSourceDesc, mMetadata, mFlags);
+ return mProvider.build_impl();
}
}
}
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 768d044..5917190 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -198,16 +198,25 @@
// Ideally it's better to make it inner class of service to enforce, it violates API
// guideline that Builders should be the inner class of the building target.
public Builder(@NonNull MediaLibraryService2 service,
- @NonNull MediaPlayerBase player,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull MediaLibrarySessionCallback callback) {
super((instance) -> ApiLoader.getProvider(service)
- .createMediaLibraryService2Builder(service, (Builder) instance, player,
+ .createMediaLibraryService2Builder(service, (Builder) instance,
callbackExecutor, callback));
}
@Override
- public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ public Builder setPlayer(@NonNull MediaPlayerBase player) {
+ return super.setPlayer(player);
+ }
+
+ @Override
+ public Builder setPlaylistController(@NonNull MediaPlaylistController mplc) {
+ return super.setPlaylistController(mplc);
+ }
+
+ @Override
+ public Builder setVolumeProvider(@NonNull VolumeProvider2 volumeProvider) {
return super.setVolumeProvider(volumeProvider);
}
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index b363831..07367bb 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -40,203 +40,347 @@
// TODO(jaewan): Add @see for APIs from MediaDescription
public final class MediaMetadata2 {
/**
- * The title of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the title of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
/**
- * The artist of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the artist of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
/**
- * The duration of the media in ms. A negative duration indicates that the
- * duration is unknown (or infinite).
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * duration of the media in ms. A negative duration indicates that the duration is unknown
+ * (or infinite).
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
/**
- * The album title for the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the album title for the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
/**
- * The author of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the author of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
/**
- * The writer of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the writer of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
/**
- * The composer of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the composer of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
/**
- * The compilation status of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the compilation status of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
/**
- * The date the media was created or published. The format is unspecified
- * but RFC 3339 is recommended.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the date the media was created or published.
+ * The format is unspecified but RFC 3339 is recommended.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
/**
- * The year the media was created or published as a long.
+ * The metadata key for a {@link Long} typed value to retrieve the information about the year
+ * the media was created or published.
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
/**
- * The genre of the media.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the genre of the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
/**
- * The track number for the media.
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * track number for the media.
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
/**
- * The number of tracks in the media's original source.
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * number of tracks in the media's original source.
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
/**
- * The disc number for the media's original source.
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * disc number for the media's original source.
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
/**
- * The artist for the album of the media's original source.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the artist for the album of the media's original source.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
/**
- * The artwork for the media as a {@link Bitmap}.
+ * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
+ * artwork for the media.
+ * The artwork should be relatively small and may be scaled down if it is too large.
+ * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead.
*
- * The artwork should be relatively small and may be scaled down
- * if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_ART_URI} should be used instead.
+ * @see Builder#putBitmap(String, Bitmap)
+ * @see #getBitmap(String)
*/
public static final String METADATA_KEY_ART = "android.media.metadata.ART";
/**
- * The artwork for the media as a Uri style String.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about Uri of the artwork for the media.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
/**
- * The artwork for the album of the media's original source as a
- * {@link Bitmap}.
- * The artwork should be relatively small and may be scaled down
- * if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
+ * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
+ * artwork for the album of the media's original source.
+ * The artwork should be relatively small and may be scaled down if it is too large.
+ * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
+ *
+ * @see Builder#putBitmap(String, Bitmap)
+ * @see #getBitmap(String)
*/
public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
/**
- * The artwork for the album of the media's original source as a Uri style
- * String.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the Uri of the artwork for the album of the media's original source.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
/**
- * The user's rating for the media.
+ * The metadata key for a {@link Rating2} typed value to retrieve the information about the
+ * user's rating for the media.
*
- * @see Rating
+ * @see Builder#putRating(String, Rating2)
+ * @see #getRating(String)
*/
public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
/**
- * The overall rating for the media.
+ * The metadata key for a {@link Rating2} typed value to retrieve the information about the
+ * overall rating for the media.
*
- * @see Rating
+ * @see Builder#putRating(String, Rating2)
+ * @see #getRating(String)
*/
public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
/**
- * A title that is suitable for display to the user. This will generally be
- * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
- * When displaying media described by this metadata this should be preferred
- * if present.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the title that is suitable for display to the user.
+ * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
+ * When displaying media described by this metadata, this should be preferred if present.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
/**
- * A subtitle that is suitable for display to the user. When displaying a
- * second line for media described by this metadata this should be preferred
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the subtitle that is suitable for display to the user.
+ * When displaying a second line for media described by this metadata, this should be preferred
* to other fields if present.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_DISPLAY_SUBTITLE
= "android.media.metadata.DISPLAY_SUBTITLE";
/**
- * A description that is suitable for display to the user. When displaying
- * more information for media described by this metadata this should be
- * preferred to other fields if present.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the description that is suitable for display to the user.
+ * When displaying more information for media described by this metadata,
+ * this should be preferred to other fields if present.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_DISPLAY_DESCRIPTION
= "android.media.metadata.DISPLAY_DESCRIPTION";
/**
- * An icon or thumbnail that is suitable for display to the user. When
- * displaying an icon for media described by this metadata this should be
- * preferred to other fields if present. This must be a {@link Bitmap}.
+ * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon
+ * or thumbnail that is suitable for display to the user.
+ * When displaying an icon for media described by this metadata, this should be preferred to
+ * other fields if present.
+ * <p>
+ * The icon should be relatively small and may be scaled down if it is too large.
+ * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
*
- * The icon should be relatively small and may be scaled down
- * if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
+ * @see Builder#putBitmap(String, Bitmap)
+ * @see #getBitmap(String)
*/
- public static final String METADATA_KEY_DISPLAY_ICON
- = "android.media.metadata.DISPLAY_ICON";
+ public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
/**
- * An icon or thumbnail that is suitable for display to the user. When
- * displaying more information for media described by this metadata the
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the Uri of icon or thumbnail that is suitable for display to the user.
+ * When displaying more information for media described by this metadata, the
* display description should be preferred to other fields when present.
- * This must be a Uri style String.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_DISPLAY_ICON_URI
= "android.media.metadata.DISPLAY_ICON_URI";
/**
- * A String key for identifying the content. This value is specific to the
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the media ID of the content. This value is specific to the
* service providing the content. If used, this should be a persistent
* unique key for the underlying content. It may be used with
* {@link MediaController2#playFromMediaId(String, Bundle)}
* to initiate playback when provided by a {@link MediaBrowser2} connected to
* the same app.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
/**
- * A Uri formatted String representing the content. This value is specific to the
- * service providing the content. It may be used with
- * {@link MediaController2#playFromUri(Uri, Bundle)}
- * to initiate playback when provided by a {@link MediaBrowser2} connected to
- * the same app.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the Uri of the content. This value is specific to the service providing the
+ * content. It may be used with {@link MediaController2#playFromUri(Uri, Bundle)}
+ * to initiate playback when provided by a {@link MediaBrowser2} connected to the same app.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
/**
- * The radio frequency in Float format if this metdata representing radio content.
+ * The metadata key for a {@link Float} typed value to retrieve the information about the
+ * radio frequency if this metadata represents radio content.
+ *
+ * @see Builder#putFloat(String, float)
+ * @see #getFloat(String)
*/
public static final String METADATA_KEY_RADIO_FREQUENCY =
"android.media.metadata.RADIO_FREQUENCY";
/**
- * The radio callsign in String format if this metdata representing radio content.
+ * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+ * information about the radio callsign if this metadata represents radio content.
+ *
+ * @see Builder#putText(String, CharSequence)
+ * @see Builder#putString(String, String)
+ * @see #getText(String)
+ * @see #getString(String)
*/
public static final String METADATA_KEY_RADIO_CALLSIGN =
"android.media.metadata.RADIO_CALLSIGN";
/**
- * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
* AVRCP 1.5. It should be one of the following:
* <ul>
* <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
@@ -247,6 +391,9 @@
* <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
* <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
* </ul>
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_BT_FOLDER_TYPE
= "android.media.metadata.BT_FOLDER_TYPE";
@@ -294,14 +441,19 @@
public static final long BT_FOLDER_TYPE_YEARS = 6;
/**
- * Whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A
- * value of 1 or non-zero indicates it is an advertisement. If not specified, this value is set
- * to 0 by default.
+ * The metadata key for a {@link Long} typed value to retrieve the information about whether
+ * the media is an advertisement. A value of 0 indicates it is not an advertisement.
+ * A value of 1 or non-zero indicates it is an advertisement.
+ * If not specified, this value is set to 0 by default.
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
/**
- * The download status of the media which will be used for later offline playback. It should be
+ * The metadata key for a {@link Long} typed value to retrieve the information about the
+ * download status of the media which will be used for later offline playback. It should be
* one of the following:
*
* <ul>
@@ -309,6 +461,9 @@
* <li>{@link #STATUS_DOWNLOADING}</li>
* <li>{@link #STATUS_DOWNLOADED}</li>
* </ul>
+ *
+ * @see Builder#putLong(String, long)
+ * @see #getLong(String)
*/
public static final String METADATA_KEY_DOWNLOAD_STATUS =
"android.media.metadata.DOWNLOAD_STATUS";
@@ -588,6 +743,7 @@
* <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
* <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
* <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
+ * <li>{@link #METADATA_KEY_RADIO_CALLSIGN}</li>
* </ul>
*
* @param key The key for referencing this value
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 1446660..e0047d6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -22,15 +22,10 @@
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.SurfaceTexture;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.view.Surface;
-import android.view.SurfaceHolder;
import android.media.MediaDrm;
import android.media.MediaFormat;
import android.media.MediaPlayer2Impl;
+import android.media.MediaPlayerBase;
import android.media.MediaTimeProvider;
import android.media.PlaybackParams;
import android.media.SubtitleController;
@@ -38,6 +33,12 @@
import android.media.SubtitleData;
import android.media.SubtitleTrack.RenderingWidget;
import android.media.SyncParams;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.view.Surface;
+import android.view.SurfaceHolder;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -45,7 +46,6 @@
import java.lang.AutoCloseable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import java.util.List;
import java.util.Map;
@@ -91,7 +91,7 @@
* <p>From this state diagram, one can see that a MediaPlayer2 object has the
* following states:</p>
* <ul>
- * <li>When a MediaPlayer2 object is just created using <code>new</code> or
+ * <li>When a MediaPlayer2 object is just created using <code>create</code> or
* after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
* {@link #close()} is called, it is in the <em>End</em> state. Between these
* two states is the life cycle of the MediaPlayer2 object.
@@ -100,9 +100,9 @@
* as {@link #getCurrentPosition()},
* {@link #getDuration()}, {@link #getVideoHeight()},
* {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- * {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
+ * {@link #setPlayerVolume(float)}, {@link #pause()}, {@link #play()},
* {@link #seekTo(long, int)} or
- * {@link #prepareAsync()} in the <em>Idle</em> state.
+ * {@link #prepare()} in the <em>Idle</em> state.
* <li>It is also recommended that once
* a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
* so that resources used by the internal player engine associated with the
@@ -126,9 +126,9 @@
* these circumstances. Sometimes, due to programming errors, invoking a playback
* control operation in an invalid state may also occur. Under all these
* error conditions, the internal player engine invokes a user supplied
- * EventCallback.onError() method if an EventCallback has been
+ * MediaPlayer2EventCallback.onError() method if an MediaPlayer2EventCallback has been
* registered beforehand via
- * {@link #registerEventCallback(Executor, EventCallback)}.
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
* <ul>
* <li>It is important to note that once an error occurs, the
* MediaPlayer2 object enters the <em>Error</em> state (except as noted
@@ -142,7 +142,7 @@
* the internal player engine.</li>
* <li>IllegalStateException is
* thrown to prevent programming errors such as calling
- * {@link #prepareAsync()}, {@link #setDataSource(DataSourceDesc)}, or
+ * {@link #prepare()}, {@link #setDataSource(DataSourceDesc)}, or
* {@code setPlaylist} methods in an invalid state. </li>
* </ul>
* </li>
@@ -164,21 +164,21 @@
* before playback can be started.
* <ul>
* <li>There are an asynchronous way that the <em>Prepared</em> state can be reached:
- * a call to {@link #prepareAsync()} (asynchronous) which
+ * a call to {@link #prepare()} (asynchronous) which
* first transfers the object to the <em>Preparing</em> state after the
* call returns (which occurs almost right way) while the internal
* player engine continues working on the rest of preparation work
* until the preparation work completes. When the preparation completes,
* the internal player engine then calls a user supplied callback method,
- * onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED}, if an
- * EventCallback is registered beforehand via
- * {@link #registerEventCallback(Executor, EventCallback)}.</li>
+ * onInfo() of the MediaPlayer2EventCallback interface with {@link #MEDIA_INFO_PREPARED},
+ * if an MediaPlayer2EventCallback is registered beforehand via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
* <li>It is important to note that
* the <em>Preparing</em> state is a transient state, and the behavior
* of calling any method with side effect while a MediaPlayer2 object is
* in the <em>Preparing</em> state is undefined.</li>
* <li>An IllegalStateException is
- * thrown if {@link #prepareAsync()} is called in
+ * thrown if {@link #prepare()} is called in
* any other state.</li>
* <li>While in the <em>Prepared</em> state, properties
* such as audio/sound volume, screenOnWhilePlaying, looping can be
@@ -187,13 +187,14 @@
* </li>
* <li>To start the playback, {@link #play()} must be called. After
* {@link #play()} returns successfully, the MediaPlayer2 object is in the
- * <em>Started</em> state. {@link #isPlaying()} can be called to test
+ * <em>Started</em> state. {@link #getPlayerState()} can be called to test
* whether the MediaPlayer2 object is in the <em>Started</em> state.
* <ul>
* <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied EventCallback.onBufferingUpdate() callback
- * method if an EventCallback has been registered beforehand
- * via {@link #registerEventCallback(Executor, EventCallback)}.
+ * a user supplied callback method MediaPlayer2EventCallback.onInfo() with
+ * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an MediaPlayer2EventCallback has been
+ * registered beforehand via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
* This callback allows applications to keep track of the buffering status
* while streaming audio/video.</li>
* <li>Calling {@link #play()} has not effect
@@ -206,7 +207,7 @@
* <em>Paused</em> state. Note that the transition from the <em>Started</em>
* state to the <em>Paused</em> state and vice versa happens
* asynchronously in the player engine. It may take some time before
- * the state is updated in calls to {@link #isPlaying()}, and it can be
+ * the state is updated in calls to {@link #getPlayerState()}, and it can be
* a number of seconds in the case of streamed content.
* <ul>
* <li>Calling {@link #play()} to resume playback for a paused
@@ -225,9 +226,9 @@
* call returns right away, the actual seek operation may take a while to
* finish, especially for audio/video being streamed. When the actual
* seek operation completes, the internal player engine calls a user
- * supplied EventCallback.onInfo() with {@link #MEDIA_INFO_COMPLETE_CALL_SEEK}
- * if an EventCallback has been registered beforehand via
- * {@link #registerEventCallback(Executor, EventCallback)}.</li>
+ * supplied MediaPlayer2EventCallback.onCallComplete() with {@link #MEDIA_CALL_SEEK_TO}
+ * if an MediaPlayer2EventCallback has been registered beforehand via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
* <li>Please
* note that {@link #seekTo(long, int)} can also be called in the other states,
* such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
@@ -243,15 +244,13 @@
* </li>
* <li>When the playback reaches the end of stream, the playback completes.
* <ul>
- * <li>If the looping mode was being set to one of the values of
- * {@link #LOOPING_MODE_FULL}, {@link #LOOPING_MODE_SINGLE} or
- * {@link #LOOPING_MODE_SHUFFLE} with
- * {@link #setLoopingMode(int)}, the MediaPlayer2 object shall remain in
- * the <em>Started</em> state.</li>
+ * <li>If current source is set to loop by {@link #loopCurrent(boolean)},
+ * the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
* <li>If the looping mode was set to <var>false
* </var>, the player engine calls a user supplied callback method,
- * EventCallback.onCompletion(), if an EventCallback is registered
- * beforehand via {@link #registerEventCallback(Executor, EventCallback)}.
+ * MediaPlayer2EventCallback.onCompletion(), if an MediaPlayer2EventCallback is
+ * registered beforehand via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
* The invoke of the callback signals that the object is now in the <em>
* PlaybackCompleted</em> state.</li>
* <li>While in the <em>PlaybackCompleted</em>
@@ -305,7 +304,7 @@
* <td>Successful invoke of this method in a valid state does not change
* the state. Calling this method in an invalid state transfers the
* object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>isPlaying </p></td>
+ * <tr><td>getPlayerState </p></td>
* <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
* PlaybackCompleted}</p></td>
* <td>{Error}</p></td>
@@ -318,7 +317,7 @@
* <td>Successful invoke of this method in a valid state transfers the
* object to the <em>Paused</em> state. Calling this method in an
* invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepareAsync </p></td>
+ * <tr><td>prepare </p></td>
* <td>{Initialized, Stopped} </p></td>
* <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
* <td>Successful invoke of this method in a valid state transfers the
@@ -345,7 +344,7 @@
* <td>{Error}</p></td>
* <td>Successful invoke of this method does not change the state. In order for the
* target audio attributes type to become effective, this method must be called before
- * prepareAsync().</p></td></tr>
+ * prepare().</p></td></tr>
* <tr><td>setAudioSessionId </p></td>
* <td>{Idle} </p></td>
* <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
@@ -359,7 +358,7 @@
* <td>{Error}</p></td>
* <td>Successful invoke of this method does not change the state. In order for the
* target audio stream type to become effective, this method must be called before
- * prepareAsync().</p></td></tr>
+ * prepare().</p></td></tr>
* <tr><td>setAuxEffectSendLevel </p></td>
* <td>any</p></td>
* <td>{} </p></td>
@@ -388,7 +387,7 @@
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setLoopingMode </p></td>
+ * <tr><td>loopCurrent </p></td>
* <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
* PlaybackCompleted}</p></td>
* <td>{Error}</p></td>
@@ -400,12 +399,12 @@
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>registerDrmEventCallback </p></td>
+ * <tr><td>setDrmEventCallback </p></td>
* <td>any </p></td>
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>registerEventCallback </p></td>
+ * <tr><td>setMediaPlayer2EventCallback </p></td>
* <td>any </p></td>
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
@@ -415,7 +414,7 @@
* <td>{Idle, Stopped} </p></td>
* <td>This method will change state in some cases, depending on when it's called.
* </p></td></tr>
- * <tr><td>setVolume </p></td>
+ * <tr><td>setPlayerVolume </p></td>
* <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
* PlaybackCompleted}</p></td>
* <td>{Error}</p></td>
@@ -463,51 +462,17 @@
* possible runtime errors during playback or streaming. Registration for
* these events is done by properly setting the appropriate listeners (via calls
* to
- * {@link #registerEventCallback(Executor, EventCallback)},
- * {@link #registerDrmEventCallback(Executor, DrmEventCallback)}).
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)},
+ * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
* In order to receive the respective callback
* associated with these listeners, applications are required to create
* MediaPlayer2 objects on a thread with its own Looper running (main UI
* thread by default has a Looper running).
*
*/
-public abstract class MediaPlayer2 implements SubtitleController.Listener
- , AudioRouting
- , AutoCloseable
-{
- /**
- Constant to retrieve only the new metadata since the last
- call.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean METADATA_UPDATE_ONLY = true;
-
- /**
- Constant to retrieve all the metadata.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean METADATA_ALL = false;
-
- /**
- Constant to enable the metadata filter during retrieval.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean APPLY_METADATA_FILTER = true;
-
- /**
- Constant to disable the metadata filter during retrieval.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean BYPASS_METADATA_FILTER = false;
-
+public abstract class MediaPlayer2 extends MediaPlayerBase
+ implements SubtitleController.Listener
+ , AudioRouting {
/**
* Create a MediaPlayer2 object.
*
@@ -525,6 +490,256 @@
public MediaPlayer2() { }
/**
+ * Releases the resources held by this {@code MediaPlayer2} object.
+ *
+ * It is considered good practice to call this method when you're
+ * done using the MediaPlayer2. In particular, whenever an Activity
+ * of an application is paused (its onPause() method is called),
+ * or stopped (its onStop() method is called), this method should be
+ * invoked to release the MediaPlayer2 object, unless the application
+ * has a special need to keep the object around. In addition to
+ * unnecessary resources (such as memory and instances of codecs)
+ * being held, failure to call this method immediately if a
+ * MediaPlayer2 object is no longer needed may also lead to
+ * continuous battery consumption for mobile devices, and playback
+ * failure for other applications if no multiple instances of the
+ * same codec are supported on a device. Even if multiple instances
+ * of the same codec are supported, some performance degradation
+ * may be expected when unnecessary multiple instances are used
+ * at the same time.
+ *
+ * {@code close()} may be safely called after a prior {@code close()}.
+ * This class implements the Java {@code AutoCloseable} interface and
+ * may be used with try-with-resources.
+ */
+ @Override
+ public abstract void close();
+
+ /**
+ * Starts or resumes playback. If playback had previously been paused,
+ * playback will continue from where it was paused. If playback had
+ * reached end of stream and been paused, or never started before,
+ * playback will start at the beginning. If the source had not been
+ * prepared, the player will prepare the source and play.
+ *
+ */
+ @Override
+ public abstract void play();
+
+ /**
+ * Prepares the player for playback, asynchronously.
+ *
+ * After setting the datasource and the display surface, you need to
+ * call prepare().
+ *
+ */
+ @Override
+ public abstract void prepare();
+
+ /**
+ * Pauses playback. Call play() to resume.
+ */
+ @Override
+ public abstract void pause();
+
+ /**
+ * Tries to play next data source if applicable.
+ */
+ @Override
+ public abstract void skipToNext();
+
+ /**
+ * Moves the media to specified time position.
+ * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
+ *
+ * @param msec the offset in milliseconds from the start to seek to
+ */
+ @Override
+ public void seekTo(long msec) {
+ seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
+ }
+
+ /**
+ * Gets the current playback position.
+ *
+ * @return the current position in milliseconds
+ */
+ @Override
+ public abstract long getCurrentPosition();
+
+ /**
+ * Gets the duration of the file.
+ *
+ * @return the duration in milliseconds, if no duration is available
+ * (for example, if streaming live content), -1 is returned.
+ */
+ @Override
+ public abstract long getDuration();
+
+ /**
+ * Gets the current buffered media source position received through progressive downloading.
+ * The received buffering percentage indicates how much of the content has been buffered
+ * or played. For example a buffering update of 80 percent when half the content
+ * has already been played indicates that the next 30 percent of the
+ * content to play has been buffered.
+ *
+ * @return the current buffered media source position in milliseconds
+ */
+ @Override
+ public abstract long getBufferedPosition();
+
+ /**
+ * Gets the current player state.
+ *
+ * @return the current player state.
+ */
+ @Override
+ public abstract @PlayerState int getPlayerState();
+
+ /**
+ * Gets the current buffering state of the player.
+ * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+ * buffered.
+ * @return the buffering state, one of the following:
+ */
+ @Override
+ public abstract @BuffState int getBufferingState();
+
+ /**
+ * Sets the audio attributes for this MediaPlayer2.
+ * See {@link AudioAttributes} for how to build and configure an instance of this class.
+ * You must call this method before {@link #prepare()} in order
+ * for the audio attributes to become effective thereafter.
+ * @param attributes a non-null set of audio attributes
+ */
+ @Override
+ public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+
+ /**
+ * Gets the audio attributes for this MediaPlayer2.
+ * @return attributes a set of audio attributes
+ */
+ @Override
+ public abstract @Nullable AudioAttributes getAudioAttributes();
+
+ /**
+ * Sets the data source as described by a DataSourceDesc.
+ *
+ * @param dsd the descriptor of data source you want to play
+ */
+ @Override
+ public abstract void setDataSource(@NonNull DataSourceDesc dsd);
+
+ /**
+ * Sets a single data source as described by a DataSourceDesc which will be played
+ * after current data source is finished.
+ *
+ * @param dsd the descriptor of data source you want to play after current one
+ */
+ @Override
+ public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
+
+ /**
+ * Sets a list of data sources to be played sequentially after current data source is done.
+ *
+ * @param dsds the list of data sources you want to play after current one
+ */
+ @Override
+ public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
+
+ /**
+ * Gets the current data source as described by a DataSourceDesc.
+ *
+ * @return the current DataSourceDesc
+ */
+ @Override
+ public abstract @NonNull DataSourceDesc getCurrentDataSource();
+
+ /**
+ * Configures the player to loop on the current data source.
+ * @param loop true if the current data source is meant to loop.
+ */
+ @Override
+ public abstract void loopCurrent(boolean loop);
+
+ /**
+ * Sets the playback speed.
+ * A value of 1.0f is the default playback value.
+ * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+ * before using negative values.<br>
+ * After changing the playback speed, it is recommended to query the actual speed supported
+ * by the player, see {@link #getPlaybackSpeed()}.
+ * @param speed the desired playback speed
+ */
+ @Override
+ public abstract void setPlaybackSpeed(float speed);
+
+ /**
+ * Returns the actual playback speed to be used by the player when playing.
+ * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+ * @return the actual playback speed
+ */
+ @Override
+ public float getPlaybackSpeed() {
+ return 1.0f;
+ }
+
+ /**
+ * Indicates whether reverse playback is supported.
+ * Reverse playback is indicated by negative playback speeds, see
+ * {@link #setPlaybackSpeed(float)}.
+ * @return true if reverse playback is supported.
+ */
+ @Override
+ public boolean isReversePlaybackSupported() {
+ return false;
+ }
+
+ /**
+ * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+ * on the audio samples.
+ * Note that this volume is specific to the player, and is separate from stream volume
+ * used across the platform.<br>
+ * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+ * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+ * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+ */
+ @Override
+ public abstract void setPlayerVolume(float volume);
+
+ /**
+ * Returns the current volume of this player to this player.
+ * Note that it does not take into account the associated stream volume.
+ * @return the player volume.
+ */
+ @Override
+ public abstract float getPlayerVolume();
+
+ /**
+ * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
+ */
+ @Override
+ public float getMaxPlayerVolume() {
+ return 1.0f;
+ }
+
+ /**
+ * Adds a callback to be notified of events for this player.
+ * @param e the {@link Executor} to be used for the events.
+ * @param cb the callback to receive the events.
+ */
+ @Override
+ public abstract void registerPlayerEventCallback(@NonNull Executor e,
+ @NonNull PlayerEventCallback cb);
+
+ /**
+ * Removes a previously registered callback for player events
+ * @param cb the callback to remove
+ */
+ @Override
+ public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
+
+ /**
* Create a request parcel which can be routed to the native media
* player using {@link #invoke(Parcel, Parcel)}. The Parcel
* returned has the proper InterfaceToken set. The caller should
@@ -556,6 +771,19 @@
public void invoke(Parcel request, Parcel reply) { }
/**
+ * Insert a task in the command queue to help the client to identify whether a batch
+ * of commands has been finished. When this command is processed, a notification
+ * {@code MediaPlayer2EventCallback.onCommandLabelReached} will be fired with the
+ * given {@code label}.
+ *
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+ *
+ * @param label An application specific Object used to help to identify the completeness
+ * of a batch of commands.
+ */
+ public void notifyWhenCommandLabelReached(Object label) { }
+
+ /**
* Sets the {@link SurfaceHolder} to use for displaying the video
* portion of the media.
*
@@ -642,188 +870,6 @@
public abstract void clearPendingCommands();
/**
- * Sets the data source as described by a DataSourceDesc.
- *
- * @param dsd the descriptor of data source you want to play
- * @throws IllegalStateException if it is called in an invalid state
- * @throws NullPointerException if dsd is null
- */
- public abstract void setDataSource(@NonNull DataSourceDesc dsd) throws IOException;
-
- /**
- * Gets the current data source as described by a DataSourceDesc.
- *
- * @return the current DataSourceDesc
- */
- public abstract DataSourceDesc getCurrentDataSource();
-
- /**
- * Sets the play list.
- *
- * If startIndex falls outside play list range, it will be clamped to the nearest index
- * in the play list.
- *
- * @param pl the play list of data source you want to play
- * @param startIndex the index of the DataSourceDesc in the play list you want to play first
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if pl is null or empty, or pl contains null DataSourceDesc
- */
- public abstract void setPlaylist(@NonNull List<DataSourceDesc> pl, int startIndex)
- throws IOException;
-
- /**
- * Gets a copy of the play list.
- *
- * @return a copy of the play list used by {@link MediaPlayer2}
- */
- public abstract List<DataSourceDesc> getPlaylist();
-
- /**
- * Sets the index of current DataSourceDesc in the play list to be played.
- *
- * @param index the index of DataSourceDesc in the play list you want to play
- * @throws IllegalArgumentException if the play list is null
- * @throws NullPointerException if index is outside play list range
- */
- public abstract void setCurrentPlaylistItem(int index);
-
- /**
- * Sets the index of next-to-be-played DataSourceDesc in the play list.
- *
- * @param index the index of next-to-be-played DataSourceDesc in the play list
- * @throws IllegalArgumentException if the play list is null
- * @throws NullPointerException if index is outside play list range
- */
- public abstract void setNextPlaylistItem(int index);
-
- /**
- * Gets the current index of play list.
- *
- * @return the index of the current DataSourceDesc in the play list
- */
- public abstract int getCurrentPlaylistItemIndex();
-
- /**
- * Specifies a playback looping mode. The source will not be played in looping mode.
- */
- public static final int LOOPING_MODE_NONE = 0;
- /**
- * Specifies a playback looping mode. The full list of source will be played in looping mode,
- * and in the order specified in the play list.
- */
- public static final int LOOPING_MODE_FULL = 1;
- /**
- * Specifies a playback looping mode. The current DataSourceDesc will be played in looping mode.
- */
- public static final int LOOPING_MODE_SINGLE = 2;
- /**
- * Specifies a playback looping mode. The full list of source will be played in looping mode,
- * and in a random order.
- */
- public static final int LOOPING_MODE_SHUFFLE = 3;
-
- /** @hide */
- @IntDef(
- value = {
- LOOPING_MODE_NONE,
- LOOPING_MODE_FULL,
- LOOPING_MODE_SINGLE,
- LOOPING_MODE_SHUFFLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LoopingMode {}
-
- /**
- * Sets the looping mode of the play list.
- * The mode shall be one of {@link #LOOPING_MODE_NONE}, {@link #LOOPING_MODE_FULL},
- * {@link #LOOPING_MODE_SINGLE}, {@link #LOOPING_MODE_SHUFFLE}.
- *
- * @param mode the mode in which the play list will be played
- * @throws IllegalArgumentException if mode is not supported
- */
- public abstract void setLoopingMode(@LoopingMode int mode);
-
- /**
- * Gets the looping mode of play list.
- *
- * @return the looping mode of the play list
- */
- public abstract int getLoopingMode();
-
- /**
- * Moves the DataSourceDesc at indexFrom in the play list to indexTo.
- *
- * @throws IllegalArgumentException if the play list is null
- * @throws IndexOutOfBoundsException if indexFrom or indexTo is outside play list range
- */
- public abstract void movePlaylistItem(int indexFrom, int indexTo);
-
- /**
- * Removes the DataSourceDesc at index in the play list.
- *
- * If index is same as the current index of the play list, current DataSourceDesc
- * will be stopped and playback moves to next source in the list.
- *
- * @return the removed DataSourceDesc at index in the play list
- * @throws IllegalArgumentException if the play list is null
- * @throws IndexOutOfBoundsException if index is outside play list range
- */
- public abstract DataSourceDesc removePlaylistItem(int index);
-
- /**
- * Inserts the DataSourceDesc to the play list at position index.
- *
- * This will not change the DataSourceDesc currently being played.
- * If index is less than or equal to the current index of the play list,
- * the current index of the play list will be incremented correspondingly.
- *
- * @param index the index you want to add dsd to the play list
- * @param dsd the descriptor of data source you want to add to the play list
- * @throws IndexOutOfBoundsException if index is outside play list range
- * @throws NullPointerException if dsd is null
- */
- public abstract void addPlaylistItem(int index, DataSourceDesc dsd);
-
- /**
- * replaces the DataSourceDesc at index in the play list with given dsd.
- *
- * When index is same as the current index of the play list, the current source
- * will be stopped and the new source will be played, except that if new
- * and old source only differ on end position and current media position is
- * smaller then the new end position.
- *
- * This will not change the DataSourceDesc currently being played.
- * If index is less than or equal to the current index of the play list,
- * the current index of the play list will be incremented correspondingly.
- *
- * @param index the index you want to add dsd to the play list
- * @param dsd the descriptor of data source you want to add to the play list
- * @throws IndexOutOfBoundsException if index is outside play list range
- * @throws NullPointerException if dsd is null
- */
- public abstract DataSourceDesc editPlaylistItem(int index, DataSourceDesc dsd);
-
- /**
- * Prepares the player for playback, asynchronously.
- *
- * After setting the datasource and the display surface, you need to
- * call prepareAsync().
- *
- * @throws IllegalStateException if it is called in an invalid state
- */
- public abstract void prepareAsync();
-
- /**
- * Starts or resumes playback. If playback had previously been paused,
- * playback will continue from where it was paused. If playback had
- * been stopped, or never started before, playback will start at the
- * beginning.
- *
- * @throws IllegalStateException if it is called in an invalid state
- */
- public abstract void play();
-
- /**
* Stops playback after playback has been started or paused.
*
* @throws IllegalStateException if the internal player engine has not been
@@ -832,14 +878,6 @@
*/
public void stop() { }
- /**
- * Pauses playback. Call play() to resume.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
- public abstract void pause();
-
//--------------------------------------------------------------------------
// Explicit Routing
//--------------------
@@ -927,9 +965,10 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #registerEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the width is available.
+ * yet. The {@code MediaPlayer2EventCallback} can be registered via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+ * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * is available.
*/
public abstract int getVideoWidth();
@@ -938,9 +977,9 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #registerEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the height is available.
+ * yet. The {@code MediaPlayer2EventCallback} can be registered via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+ * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height is available.
*/
public abstract int getVideoHeight();
@@ -962,10 +1001,59 @@
* @return true if currently playing, false otherwise
* @throws IllegalStateException if the internal player engine has not been
* initialized or has been released.
+ * @hide
*/
public abstract boolean isPlaying();
/**
+ * MediaPlayer2 has not been prepared or just has been reset.
+ * In this state, MediaPlayer2 doesn't fetch data.
+ */
+ public static final int MEDIAPLAYER2_STATE_IDLE = 1;
+
+ /**
+ * MediaPlayer2 has been just prepared.
+ * In this state, MediaPlayer2 just fetches data from media source,
+ * but doesn't actively render data.
+ */
+ public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
+
+ /**
+ * MediaPlayer2 is paused.
+ * In this state, MediaPlayer2 doesn't actively render data.
+ */
+ public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
+
+ /**
+ * MediaPlayer2 is actively playing back data.
+ */
+ public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
+
+ /**
+ * MediaPlayer2 has hit some fatal error and cannot continue playback.
+ */
+ public static final int MEDIAPLAYER2_STATE_ERROR = 5;
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ MEDIAPLAYER2_STATE_IDLE,
+ MEDIAPLAYER2_STATE_PREPARED,
+ MEDIAPLAYER2_STATE_PAUSED,
+ MEDIAPLAYER2_STATE_PLAYING,
+ MEDIAPLAYER2_STATE_ERROR })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaPlayer2State {}
+
+ /**
+ * Gets the current MediaPlayer2 state.
+ *
+ * @return the current MediaPlayer2 state.
+ */
+ public abstract @MediaPlayer2State int getMediaPlayer2State();
+
+ /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -1075,10 +1163,6 @@
* non-zero speed is equivalent to calling play().
*
* @param params the playback params.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
- * @throws IllegalArgumentException if params is not supported.
*/
public abstract void setPlaybackParams(@NonNull PlaybackParams params);
@@ -1086,8 +1170,6 @@
* Gets the playback params, containing the current playback rate.
*
* @return the playback params.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
*/
@NonNull
public abstract PlaybackParams getPlaybackParams();
@@ -1096,10 +1178,6 @@
* Sets A/V sync mode.
*
* @param params the A/V sync params to apply
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if params are not supported.
*/
public abstract void setSyncParams(@NonNull SyncParams params);
@@ -1107,9 +1185,6 @@
* Gets the A/V sync mode.
*
* @return the A/V sync params
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
*/
@NonNull
public abstract SyncParams getSyncParams();
@@ -1191,9 +1266,6 @@
* or may not be a sync frame but is closest to or the same as msec.
* {@link #SEEK_CLOSEST} often has larger performance overhead compared
* to the other options if there is no sync frame located at msec.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized
- * @throws IllegalArgumentException if the mode is invalid.
*/
public abstract void seekTo(long msec, @SeekMode int mode);
@@ -1219,21 +1291,6 @@
public abstract MediaTimestamp getTimestamp();
/**
- * Gets the current playback position.
- *
- * @return the current position in milliseconds
- */
- public abstract long getCurrentPosition();
-
- /**
- * Gets the duration of the file.
- *
- * @return the duration in milliseconds, if no duration is available
- * (for example, if streaming live content), -1 is returned.
- */
- public abstract long getDuration();
-
- /**
* Gets the media metadata.
*
* @param update_only controls whether the full set of available
@@ -1280,7 +1337,7 @@
/**
* Resets the MediaPlayer2 to its uninitialized state. After calling
* this method, you will have to initialize it again by setting the
- * data source and calling prepareAsync().
+ * data source and calling prepare().
*/
public abstract void reset();
@@ -1295,31 +1352,6 @@
public void notifyAt(long mediaTimeUs) { }
/**
- * Sets the audio attributes for this MediaPlayer2.
- * See {@link AudioAttributes} for how to build and configure an instance of this class.
- * You must call this method before {@link #prepareAsync()} in order
- * for the audio attributes to become effective thereafter.
- * @param attributes a non-null set of audio attributes
- * @throws IllegalArgumentException if the attributes are null or invalid.
- */
- public abstract void setAudioAttributes(AudioAttributes attributes);
-
- /**
- * Gets the audio attributes for this MediaPlayer2.
- * @return attributes a set of audio attributes
- * @throws IllegalArgumentException if the attributes are null or invalid.
- */
- public abstract AudioAttributes getAudioAttributes();
-
- /**
- * Sets the player to be looping or non-looping.
- *
- * @param looping whether to loop or not
- * @hide
- */
- public void setLooping(boolean looping) { }
-
- /**
* Checks whether the MediaPlayer2 is looping or non-looping.
*
* @return true if the MediaPlayer2 is currently looping, false otherwise
@@ -1330,31 +1362,6 @@
}
/**
- * Sets the volume on this player.
- * This API is recommended for balancing the output of audio streams
- * within an application. Unless you are writing an application to
- * control user settings, this API should be used in preference to
- * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
- * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
- * UI controls should be scaled logarithmically.
- *
- * @param leftVolume left volume scalar
- * @param rightVolume right volume scalar
- */
- /*
- * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
- * The single parameter form below is preferred if the channel volumes don't need
- * to be set independently.
- */
- public abstract void setVolume(float leftVolume, float rightVolume);
-
- /**
- * Similar, excepts sets volume of all channels to same value.
- * @hide
- */
- public void setVolume(float volume) { }
-
- /**
* Sets the audio session ID.
*
* @param sessionId the audio session ID.
@@ -1368,8 +1375,6 @@
* However, it is possible to force this player to be part of an already existing audio session
* by calling this method.
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the sessionId is invalid.
*/
public abstract void setAudioSessionId(int sessionId);
@@ -1458,7 +1463,6 @@
* @return List of track info. The total number of tracks is the array length.
* Must be called again if an external timed text source has been added after
* addTimedTextSource method is called.
- * @throws IllegalStateException if it is called in an invalid state.
*/
public abstract List<TrackInfo> getTrackInfo();
@@ -1643,32 +1647,6 @@
*/
public abstract void deselectTrack(int index);
- /**
- * Releases the resources held by this {@code MediaPlayer2} object.
- *
- * It is considered good practice to call this method when you're
- * done using the MediaPlayer2. In particular, whenever an Activity
- * of an application is paused (its onPause() method is called),
- * or stopped (its onStop() method is called), this method should be
- * invoked to release the MediaPlayer2 object, unless the application
- * has a special need to keep the object around. In addition to
- * unnecessary resources (such as memory and instances of codecs)
- * being held, failure to call this method immediately if a
- * MediaPlayer2 object is no longer needed may also lead to
- * continuous battery consumption for mobile devices, and playback
- * failure for other applications if no multiple instances of the
- * same codec are supported on a device. Even if multiple instances
- * of the same codec are supported, some performance degradation
- * may be expected when unnecessary multiple instances are used
- * at the same time.
- *
- * {@code close()} may be safely called after a prior {@code close()}.
- * This class implements the Java {@code AutoCloseable} interface and
- * may be used with try-with-resources.
- */
- @Override
- public abstract void close();
-
/** @hide */
public MediaTimeProvider getMediaTimeProvider() {
return null;
@@ -1678,22 +1656,7 @@
* Interface definition for callbacks to be invoked when the player has the corresponding
* events.
*/
- public abstract static class EventCallback {
- /**
- * Called to update status in buffering a media source received through
- * progressive downloading. The received buffering percentage
- * indicates how much of the content has been buffered or played.
- * For example a buffering update of 80 percent when half the content
- * has already been played indicates that the next 30 percent of the
- * content to play has been buffered.
- *
- * @param mp the MediaPlayer2 the update pertains to
- * @param srcId the Id of this data source
- * @param percent the percentage (0-100) of the content
- * that has been buffered or played thus far
- */
- public void onBufferingUpdate(MediaPlayer2 mp, long srcId, int percent) { }
-
+ public abstract static class MediaPlayer2EventCallback {
/**
* Called to indicate the video size
*
@@ -1701,22 +1664,22 @@
* no display surface was set, or the value was not determined yet.
*
* @param mp the MediaPlayer2 associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param width the width of the video
* @param height the height of the video
*/
- public void onVideoSizeChanged(MediaPlayer2 mp, long srcId, int width, int height) { }
+ public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
/**
* Called to indicate an avaliable timed text
*
* @param mp the MediaPlayer2 associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param text the timed text sample which contains the text
* needed to be displayed and the display format.
* @hide
*/
- public void onTimedText(MediaPlayer2 mp, long srcId, TimedText text) { }
+ public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { }
/**
* Called to indicate avaliable timed metadata
@@ -1733,16 +1696,16 @@
* @see TimedMetaData
*
* @param mp the MediaPlayer2 associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param data the timed metadata sample associated with this event
*/
- public void onTimedMetaDataAvailable(MediaPlayer2 mp, long srcId, TimedMetaData data) { }
+ public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
/**
* Called to indicate an error.
*
* @param mp the MediaPlayer2 the error pertains to
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param what the type of error that has occurred:
* <ul>
* <li>{@link #MEDIA_ERROR_UNKNOWN}
@@ -1757,13 +1720,13 @@
* <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
* </ul>
*/
- public void onError(MediaPlayer2 mp, long srcId, int what, int extra) { }
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { }
/**
* Called to indicate an info or a warning.
*
* @param mp the MediaPlayer2 the info pertains to.
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param what the type of info or warning.
* <ul>
* <li>{@link #MEDIA_INFO_UNKNOWN}
@@ -1773,9 +1736,6 @@
* <li>{@link #MEDIA_INFO_PLAYBACK_COMPLETE}
* <li>{@link #MEDIA_INFO_PLAYLIST_END}
* <li>{@link #MEDIA_INFO_PREPARED}
- * <li>{@link #MEDIA_INFO_COMPLETE_CALL_PLAY}
- * <li>{@link #MEDIA_INFO_COMPLETE_CALL_PAUSE}
- * <li>{@link #MEDIA_INFO_COMPLETE_CALL_SEEK}
* <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
* <li>{@link #MEDIA_INFO_BUFFERING_START}
* <li>{@link #MEDIA_INFO_BUFFERING_END}
@@ -1790,25 +1750,78 @@
* @param extra an extra code, specific to the info. Typically
* implementation dependent.
*/
- public void onInfo(MediaPlayer2 mp, long srcId, int what, int extra) { }
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { }
+
+ /**
+ * Called to acknowledge an API call.
+ *
+ * @param mp the MediaPlayer2 the call was made on.
+ * @param dsd the DataSourceDesc of this data source
+ * @param what the enum for the API call.
+ * <ul>
+ * <li>{@link #MEDIA_CALL_ATTACH_AUX_EFFECT}
+ * <li>{@link #MEDIA_CALL_DESELECT_TRACK}
+ * <li>{@link #MEDIA_CALL_LOOP_CURRENT}
+ * <li>{@link #MEDIA_CALL_PAUSE}
+ * <li>{@link #MEDIA_CALL_PLAY}
+ * <li>{@link #MEDIA_CALL_PREPARE}
+ * <li>{@link #MEDIA_CALL_PREPARE_DRM}
+ * <li>{@link #MEDIA_CALL_PROVIDE_DRM_KEY_RESPONSE}
+ * <li>{@link #MEDIA_CALL_RELEASE_DRM}
+ * <li>{@link #MEDIA_CALL_RESTORE_DRM_KEYS}
+ * <li>{@link #MEDIA_CALL_SEEK_TO}
+ * <li>{@link #MEDIA_CALL_SELECT_TRACK}
+ * <li>{@link #MEDIA_CALL_SET_AUDIO_ATTRIBUTES}
+ * <li>{@link #MEDIA_CALL_SET_AUDIO_SESSION_ID}
+ * <li>{@link #MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL}
+ * <li>{@link #MEDIA_CALL_SET_DATA_SOURCE}
+ * <li>{@link #MEDIA_CALL_SET_DRM_CONFIG_HELPER}
+ * <li>{@link #MEDIA_CALL_SET_DRM_PROPERTY_STRING}
+ * <li>{@link #MEDIA_CALL_SET_NEXT_DATA_SOURCE}
+ * <li>{@link #MEDIA_CALL_SET_NEXT_DATA_SOURCES}
+ * <li>{@link #MEDIA_CALL_SET_PLAYBACK_PARAMS}
+ * <li>{@link #MEDIA_CALL_SET_PLAYBACK_SPEED}
+ * <li>{@link #MEDIA_CALL_SET_PLAYER_VOLUME}
+ * <li>{@link #MEDIA_CALL_SET_SURFACE}
+ * <li>{@link #MEDIA_CALL_SET_SYNC_PARAMS}
+ * <li>{@link #MEDIA_CALL_SKIP_TO_NEXT}
+ * </ul>
+ * @param status the returned status code for the call.
+ */
+ public void onCallComplete(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) { }
+
+ /**
+ * Called to indicate media clock has changed.
+ *
+ * @param mp the MediaPlayer2 the media time pertains to.
+ * @param dsd the DataSourceDesc of this data source
+ * @param timestamp the new media clock.
+ */
+ public void onMediaTimeChanged(MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
+
+ /**
+ * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
+ *
+ * @param mp the MediaPlayer2 {@link #notifyWhenCommandLabelReached(Object)} was called on.
+ * @param label the application specific Object given by
+ * {@link #notifyWhenCommandLabelReached(Object)}.
+ */
+ public void onCommandLabelReached(MediaPlayer2 mp, Object label) { }
}
/**
- * Register a callback to be invoked when the media source is ready
- * for playback.
+ * Sets the callback to be invoked when the media source is ready for playback.
*
* @param eventCallback the callback that will be run
* @param executor the executor through which the callback should be invoked
*/
- public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback eventCallback);
+ public abstract void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull MediaPlayer2EventCallback eventCallback);
/**
- * Unregisters an {@link EventCallback}.
- *
- * @param callback an {@link EventCallback} to unregister
+ * Clears the {@link MediaPlayer2EventCallback}.
*/
- public abstract void unregisterEventCallback(EventCallback callback);
+ public abstract void clearMediaPlayer2EventCallback();
/**
* Interface definition of a callback to be invoked when a
@@ -1835,14 +1848,14 @@
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player error.
- * @see android.media.MediaPlayer2.EventCallback.onError
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
*/
public static final int MEDIA_ERROR_UNKNOWN = 1;
/** The video is streamed and its container is not valid for progressive
* playback i.e the video's index (e.g moov atom) is not at the start of the
* file.
- * @see android.media.MediaPlayer2.EventCallback.onError
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
@@ -1858,7 +1871,7 @@
/** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
* system/core/include/utils/Errors.h
- * @see android.media.MediaPlayer2.EventCallback.onError
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
* @hide
*/
public static final int MEDIA_ERROR_SYSTEM = -2147483648;
@@ -1868,96 +1881,93 @@
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player info.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNKNOWN = 1;
/** The player switched to this datas source because it is the
- * next-to-be-played in the play list.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * next-to-be-played in the playlist.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
/** The player just pushed the very first video frame for rendering.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
/** The player just rendered the very first audio sample.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
/** The player just completed the playback of this data source.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
- /** The player just completed the playback of the full play list.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ /** The player just completed the playback of the full playlist.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYLIST_END = 6;
/** The player just prepared a data source.
- * This also serves as call completion notification for {@link #prepareAsync()}.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_PREPARED = 100;
- /** The player just completed a call {@link #play()}.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
- */
- public static final int MEDIA_INFO_COMPLETE_CALL_PLAY = 101;
-
- /** The player just completed a call {@link #pause()}.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
- */
- public static final int MEDIA_INFO_COMPLETE_CALL_PAUSE = 102;
-
- /** The player just completed a call {@link #seekTo(long, int)}.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
- */
- public static final int MEDIA_INFO_COMPLETE_CALL_SEEK = 103;
-
/** The video is too complex for the decoder: it can't decode frames fast
* enough. Possibly only the audio plays fine at this stage.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
/** MediaPlayer2 is temporarily pausing playback internally in order to
* buffer more data.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_START = 701;
/** MediaPlayer2 is resuming playback after filling buffers.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_END = 702;
/** Estimated network bandwidth information (kbps) is available; currently this event fires
* simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
* when playing network files.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
* @hide
*/
public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
+ /**
+ * Update status in buffering a media source received through progressive downloading.
+ * The received buffering percentage indicates how much of the content has been buffered
+ * or played. For example a buffering update of 80 percent when half the content
+ * has already been played indicates that the next 30 percent of the
+ * content to play has been buffered.
+ *
+ * The {@code extra} parameter in {@code MediaPlayer2EventCallback.onInfo} is the
+ * percentage (0-100) of the content that has been buffered or played thus far.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ */
+ public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
+
/** Bad interleaving means that a media has been improperly interleaved or
* not interleaved at all, e.g has all the video samples first then all the
* audio ones. Video is playing but a lot of disk seeks may be happening.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
/** The media cannot be seeked (e.g live stream)
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
/** A new set of metadata is available.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_METADATA_UPDATE = 802;
@@ -1969,33 +1979,164 @@
/** Informs that audio is not playing. Note that playback of the video
* is not interrupted.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
/** Informs that video is not playing. Note that playback of the audio
* is not interrupted.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
/** Failed to handle timed text track properly.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*
* {@hide}
*/
public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
/** Subtitle track was not supported by the media framework.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
/** Reading the subtitle track takes too long.
- * @see android.media.MediaPlayer2.EventCallback.onInfo
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
*/
public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
+ //--------------------------------------------------------------------------
+ /** The player just completed a call {@code attachAuxEffect}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_ATTACH_AUX_EFFECT = 1;
+
+ /** The player just completed a call {@code deselectTrack}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_DESELECT_TRACK = 2;
+
+ /** The player just completed a call {@code loopCurrent}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback.CallComplete
+ */
+ public static final int MEDIA_CALL_LOOP_CURRENT = 3;
+
+ /** The player just completed a call {@code pause}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback.CallComplete
+ */
+ public static final int MEDIA_CALL_PAUSE = 4;
+
+ /** The player just completed a call {@code play}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_PLAY = 5;
+
+ /** The player just completed a call {@code prepare}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_PREPARE = 6;
+
+ /** The player just completed a call {@code prepareDrm}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_PREPARE_DRM = 7;
+
+ /** The player just completed a call {@code provideDrmKeyResponse}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_PROVIDE_DRM_KEY_RESPONSE = 8;
+
+ /** The player just completed a call {@code releaseDrm}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_RELEASE_DRM = 12;
+
+ /** The player just completed a call {@code restoreDrmKeys}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_RESTORE_DRM_KEYS = 13;
+
+ /** The player just completed a call {@code seekTo}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SEEK_TO = 14;
+
+ /** The player just completed a call {@code selectTrack}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SELECT_TRACK = 15;
+
+ /** The player just completed a call {@code setAudioAttributes}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_AUDIO_ATTRIBUTES = 16;
+
+ /** The player just completed a call {@code setAudioSessionId}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_AUDIO_SESSION_ID = 17;
+
+ /** The player just completed a call {@code setAuxEffectSendLevel}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL = 18;
+
+ /** The player just completed a call {@code setDataSource}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_DATA_SOURCE = 19;
+
+ /** The player just completed a call {@code setOnDrmConfigHelper}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_DRM_CONFIG_HELPER = 20;
+
+ /** The player just completed a call {@code setDrmPropertyString}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_DRM_PROPERTY_STRING = 21;
+
+ /** The player just completed a call {@code setNextDataSource}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCE = 22;
+
+ /** The player just completed a call {@code setNextDataSources}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCES = 23;
+
+ /** The player just completed a call {@code setPlaybackParams}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_PLAYBACK_PARAMS = 24;
+
+ /** The player just completed a call {@code setPlaybackSpeed}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_PLAYBACK_SPEED = 25;
+
+ /** The player just completed a call {@code setPlayerVolume}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_PLAYER_VOLUME = 26;
+
+ /** The player just completed a call {@code setSurface}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_SURFACE = 27;
+
+ /** The player just completed a call {@code setSyncParams}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SET_SYNC_PARAMS = 28;
+
+ /** The player just completed a call {@code skipToNext}.
+ * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+ */
+ public static final int MEDIA_CALL_SKIP_TO_NEXT = 29;
+
// Modular DRM begin
@@ -2015,9 +2156,9 @@
* Called to give the app the opportunity to configure DRM before the session is created
*
* @param mp the {@code MediaPlayer2} associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
*/
- public void onDrmConfig(MediaPlayer2 mp, long srcId);
+ public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
}
/**
@@ -2039,42 +2180,40 @@
* Called to indicate DRM info is available
*
* @param mp the {@code MediaPlayer2} associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @param drmInfo DRM info of the source including PSSH, and subset
* of crypto schemes supported by this device
*/
- public void onDrmInfo(MediaPlayer2 mp, long srcId, DrmInfo drmInfo) { }
+ public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
/**
- * Called to notify the client that {@code prepareDrm} is finished and ready for key request/response.
+ * Called to notify the client that {@code prepareDrm} is finished and ready for
+ * key request/response.
*
* @param mp the {@code MediaPlayer2} associated with this callback
- * @param srcId the Id of this data source
+ * @param dsd the DataSourceDesc of this data source
* @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(MediaPlayer2 mp, long srcId, @PrepareDrmStatusCode int status) { }
+ public void onDrmPrepared(MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
}
/**
- * Register a callback to be invoked when the media source is ready
- * for playback.
+ * Sets the callback to be invoked when the media source is ready for playback.
*
* @param eventCallback the callback that will be run
* @param executor the executor through which the callback should be invoked
*/
- public abstract void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+ public abstract void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull DrmEventCallback eventCallback);
/**
- * Unregisters a {@link DrmEventCallback}.
- *
- * @param callback a {@link DrmEventCallback} to unregister
+ * Clears the {@link DrmEventCallback}.
*/
- public abstract void unregisterDrmEventCallback(DrmEventCallback callback);
+ public abstract void clearDrmEventCallback();
/**
* The status codes for {@link DrmEventCallback#onDrmPrepared} listener.
@@ -2172,14 +2311,14 @@
* A key request/response exchange occurs between the app and a license server
* to obtain or release keys used to decrypt encrypted content.
* <p>
- * getKeyRequest() is used to obtain an opaque key request byte array that is
+ * getDrmKeyRequest() is used to obtain an opaque key request byte array that is
* delivered to the license server. The opaque key request byte array is returned
* in KeyRequest.data. The recommended URL to deliver the key request to is
* returned in KeyRequest.defaultUrl.
* <p>
* After the app has received the key request response from the server,
* it should deliver to the response to the DRM engine plugin using the method
- * {@link #provideKeyResponse}.
+ * {@link #provideDrmKeyResponse}.
*
* @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
@@ -2206,22 +2345,23 @@
* @throws NoDrmSchemeException if there is no active DRM session
*/
@NonNull
- public abstract MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+ public abstract MediaDrm.KeyRequest getDrmKeyRequest(
+ @Nullable byte[] keySetId, @Nullable byte[] initData,
@Nullable String mimeType, @MediaDrm.KeyType int keyType,
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException;
/**
* A key response is received from the license server by the app, then it is
- * provided to the DRM engine plugin using provideKeyResponse. When the
+ * provided to the DRM engine plugin using provideDrmKeyResponse. When the
* response is for an offline key request, a key-set identifier is returned that
* can be used to later restore the keys to a new session with the method
- * {@ link # restoreKeys}.
+ * {@ link # restoreDrmKeys}.
* When the response is for a streaming or release request, null is returned.
*
* @param keySetId When the response is for a release request, keySetId identifies
* the saved key associated with the release request (i.e., the same keySetId
- * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the
+ * passed to the earlier {@ link # getDrmKeyRequest} call. It MUST be null when the
* response is for either streaming or offline key requests.
*
* @param response the byte array response from the server
@@ -2230,16 +2370,17 @@
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
*/
- public abstract byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
+ public abstract byte[] provideDrmKeyResponse(
+ @Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException;
/**
* Restore persisted offline keys into a new session. keySetId identifies the
- * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
+ * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
*
* @param keySetId identifies the saved key set to restore
*/
- public abstract void restoreKeys(@NonNull byte[] keySetId)
+ public abstract void restoreDrmKeys(@NonNull byte[] keySetId)
throws NoDrmSchemeException;
/**
@@ -2252,7 +2393,8 @@
* {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*/
@NonNull
- public abstract String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
+ public abstract String getDrmPropertyString(
+ @NonNull @MediaDrm.StringProperty String propertyName)
throws NoDrmSchemeException;
/**
@@ -2265,8 +2407,8 @@
* {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
* {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*/
- public abstract void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName,
- @NonNull String value)
+ public abstract void setDrmPropertyString(
+ @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
throws NoDrmSchemeException;
/**
@@ -2410,4 +2552,38 @@
public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
}
+
+ /**
+ Constant to retrieve only the new metadata since the last
+ call.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean METADATA_UPDATE_ONLY = true;
+
+ /**
+ Constant to retrieve all the metadata.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean METADATA_ALL = false;
+
+ /**
+ Constant to enable the metadata filter during retrieval.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean APPLY_METADATA_FILTER = true;
+
+ /**
+ Constant to disable the metadata filter during retrieval.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean BYPASS_METADATA_FILTER = false;
+
}
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 7794e08..50bf738 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -24,6 +24,9 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.graphics.SurfaceTexture;
+import android.media.SubtitleController.Anchor;
+import android.media.SubtitleTrack.RenderingWidget;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -45,9 +48,6 @@
import android.view.Surface;
import android.view.SurfaceHolder;
import android.widget.VideoView;
-import android.graphics.SurfaceTexture;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleTrack.RenderingWidget;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -77,7 +77,6 @@
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
@@ -87,453 +86,6 @@
/**
- * MediaPlayer2 class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
- *
- * <p>Topics covered here are:
- * <ol>
- * <li><a href="#StateDiagram">State Diagram</a>
- * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
- * <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Register informational and error callbacks</a>
- * </ol>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about how to use MediaPlayer2, read the
- * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
- * </div>
- *
- * <a name="StateDiagram"></a>
- * <h3>State Diagram</h3>
- *
- * <p>Playback control of audio/video files and streams is managed as a state
- * machine. The following diagram shows the life cycle and the states of a
- * MediaPlayer2 object driven by the supported playback control operations.
- * The ovals represent the states a MediaPlayer2 object may reside
- * in. The arcs represent the playback control operations that drive the object
- * state transition. There are two types of arcs. The arcs with a single arrow
- * head represent synchronous method calls, while those with
- * a double arrow head represent asynchronous method calls.</p>
- *
- * <p><img src="../../../images/mediaplayer_state_diagram.gif"
- * alt="MediaPlayer State diagram"
- * border="0" /></p>
- *
- * <p>From this state diagram, one can see that a MediaPlayer2 object has the
- * following states:</p>
- * <ul>
- * <li>When a MediaPlayer2 object is just created using <code>new</code> or
- * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
- * {@link #close()} is called, it is in the <em>End</em> state. Between these
- * two states is the life cycle of the MediaPlayer2 object.
- * <ul>
- * <li>There is a subtle but important difference between a newly constructed
- * MediaPlayer2 object and the MediaPlayer2 object after {@link #reset()}
- * is called. It is a programming error to invoke methods such
- * as {@link #getCurrentPosition()},
- * {@link #getDuration()}, {@link #getVideoHeight()},
- * {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- * {@link #setLooping(boolean)},
- * {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
- * {@link #seekTo(long, int)} or
- * {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
- * methods is called right after a MediaPlayer2 object is constructed,
- * the user supplied callback method OnErrorListener.onError() won't be
- * called by the internal player engine and the object state remains
- * unchanged; but if these methods are called right after {@link #reset()},
- * the user supplied callback method OnErrorListener.onError() will be
- * invoked by the internal player engine and the object will be
- * transfered to the <em>Error</em> state. </li>
- * <li>It is also recommended that once
- * a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
- * so that resources used by the internal player engine associated with the
- * MediaPlayer2 object can be released immediately. Resource may include
- * singleton resources such as hardware acceleration components and
- * failure to call {@link #close()} may cause subsequent instances of
- * MediaPlayer2 objects to fallback to software implementations or fail
- * altogether. Once the MediaPlayer2
- * object is in the <em>End</em> state, it can no longer be used and
- * there is no way to bring it back to any other state. </li>
- * <li>Furthermore,
- * the MediaPlayer2 objects created using <code>new</code> is in the
- * <em>Idle</em> state.
- * </li>
- * </ul>
- * </li>
- * <li>In general, some playback control operation may fail due to various
- * reasons, such as unsupported audio/video format, poorly interleaved
- * audio/video, resolution too high, streaming timeout, and the like.
- * Thus, error reporting and recovery is an important concern under
- * these circumstances. Sometimes, due to programming errors, invoking a playback
- * control operation in an invalid state may also occur. Under all these
- * error conditions, the internal player engine invokes a user supplied
- * EventCallback.onError() method if an EventCallback has been
- * registered beforehand via
- * {@link #registerEventCallback(Executor, EventCallback)}.
- * <ul>
- * <li>It is important to note that once an error occurs, the
- * MediaPlayer2 object enters the <em>Error</em> state (except as noted
- * above), even if an error listener has not been registered by the application.</li>
- * <li>In order to reuse a MediaPlayer2 object that is in the <em>
- * Error</em> state and recover from the error,
- * {@link #reset()} can be called to restore the object to its <em>Idle</em>
- * state.</li>
- * <li>It is good programming practice to have your application
- * register a OnErrorListener to look out for error notifications from
- * the internal player engine.</li>
- * <li>IllegalStateException is
- * thrown to prevent programming errors such as calling
- * {@link #prepareAsync()}, {@link #setDataSource(DataSourceDesc)}, or
- * {@code setPlaylist} methods in an invalid state. </li>
- * </ul>
- * </li>
- * <li>Calling
- * {@link #setDataSource(DataSourceDesc)}, or
- * {@code setPlaylist} transfers a
- * MediaPlayer2 object in the <em>Idle</em> state to the
- * <em>Initialized</em> state.
- * <ul>
- * <li>An IllegalStateException is thrown if
- * setDataSource() or setPlaylist() is called in any other state.</li>
- * <li>It is good programming
- * practice to always look out for <code>IllegalArgumentException</code>
- * and <code>IOException</code> that may be thrown from
- * <code>setDataSource</code> and <code>setPlaylist</code> methods.</li>
- * </ul>
- * </li>
- * <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
- * before playback can be started.
- * <ul>
- * <li>{@link #prepareAsync()} first transfers the object to the
- * <em>Preparing</em> state after the
- * call returns (which occurs almost right way) while the internal
- * player engine continues working on the rest of preparation work
- * until the preparation work completes. When the preparation completes,
- * the internal player engine then calls a user supplied callback method,
- * onPrepared() of the EventCallback interface, if an
- * EventCallback is registered beforehand via {@link
- * #registerEventCallback(Executor, EventCallback)}.</li>
- * <li>It is important to note that
- * the <em>Preparing</em> state is a transient state, and the behavior
- * of calling any method with side effect while a MediaPlayer2 object is
- * in the <em>Preparing</em> state is undefined.</li>
- * <li>An IllegalStateException is
- * thrown if {@link #prepareAsync()} is called in
- * any other state.</li>
- * <li>While in the <em>Prepared</em> state, properties
- * such as audio/sound volume, screenOnWhilePlaying, looping can be
- * adjusted by invoking the corresponding set methods.</li>
- * </ul>
- * </li>
- * <li>To start the playback, {@link #play()} must be called. After
- * {@link #play()} returns successfully, the MediaPlayer2 object is in the
- * <em>Started</em> state. {@link #isPlaying()} can be called to test
- * whether the MediaPlayer2 object is in the <em>Started</em> state.
- * <ul>
- * <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied EventCallback.onBufferingUpdate() callback
- * method if an EventCallback has been registered beforehand
- * via {@link #registerEventCallback(Executor, EventCallback)}.
- * This callback allows applications to keep track of the buffering status
- * while streaming audio/video.</li>
- * <li>Calling {@link #play()} has not effect
- * on a MediaPlayer2 object that is already in the <em>Started</em> state.</li>
- * </ul>
- * </li>
- * <li>Playback can be paused and stopped, and the current playback position
- * can be adjusted. Playback can be paused via {@link #pause()}. When the call to
- * {@link #pause()} returns, the MediaPlayer2 object enters the
- * <em>Paused</em> state. Note that the transition from the <em>Started</em>
- * state to the <em>Paused</em> state and vice versa happens
- * asynchronously in the player engine. It may take some time before
- * the state is updated in calls to {@link #isPlaying()}, and it can be
- * a number of seconds in the case of streamed content.
- * <ul>
- * <li>Calling {@link #play()} to resume playback for a paused
- * MediaPlayer2 object, and the resumed playback
- * position is the same as where it was paused. When the call to
- * {@link #play()} returns, the paused MediaPlayer2 object goes back to
- * the <em>Started</em> state.</li>
- * <li>Calling {@link #pause()} has no effect on
- * a MediaPlayer2 object that is already in the <em>Paused</em> state.</li>
- * </ul>
- * </li>
- * <li>The playback position can be adjusted with a call to
- * {@link #seekTo(long, int)}.
- * <ul>
- * <li>Although the asynchronuous {@link #seekTo(long, int)}
- * call returns right away, the actual seek operation may take a while to
- * finish, especially for audio/video being streamed. When the actual
- * seek operation completes, the internal player engine calls a user
- * supplied EventCallback.onSeekComplete() if an EventCallback
- * has been registered beforehand via
- * {@link #registerEventCallback(Executor, EventCallback)}.</li>
- * <li>Please
- * note that {@link #seekTo(long, int)} can also be called in the other states,
- * such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
- * </em> state. When {@link #seekTo(long, int)} is called in those states,
- * one video frame will be displayed if the stream has video and the requested
- * position is valid.
- * </li>
- * <li>Furthermore, the actual current playback position
- * can be retrieved with a call to {@link #getCurrentPosition()}, which
- * is helpful for applications such as a Music player that need to keep
- * track of the playback progress.</li>
- * </ul>
- * </li>
- * <li>When the playback reaches the end of stream, the playback completes.
- * <ul>
- * <li>If the looping mode was being set to <var>true</var>with
- * {@link #setLooping(boolean)}, the MediaPlayer2 object shall remain in
- * the <em>Started</em> state.</li>
- * <li>If the looping mode was set to <var>false
- * </var>, the player engine calls a user supplied callback method,
- * EventCallback.onCompletion(), if an EventCallback is registered
- * beforehand via {@link #registerEventCallback(Executor, EventCallback)}.
- * The invoke of the callback signals that the object is now in the <em>
- * PlaybackCompleted</em> state.</li>
- * <li>While in the <em>PlaybackCompleted</em>
- * state, calling {@link #play()} can restart the playback from the
- * beginning of the audio/video source.</li>
- * </ul>
- *
- *
- * <a name="Valid_and_Invalid_States"></a>
- * <h3>Valid and invalid states</h3>
- *
- * <table border="0" cellspacing="0" cellpadding="0">
- * <tr><td>Method Name </p></td>
- * <td>Valid Sates </p></td>
- * <td>Invalid States </p></td>
- * <td>Comments </p></td></tr>
- * <tr><td>attachAuxEffect </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Error} </p></td>
- * <td>This method must be called after setDataSource or setPlaylist.
- * Calling it does not change the object state. </p></td></tr>
- * <tr><td>getAudioSessionId </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>getCurrentPosition </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted} </p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getDuration </p></td>
- * <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Error} </p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoHeight </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoWidth </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>isPlaying </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>pause </p></td>
- * <td>{Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Paused</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepare </p></td>
- * <td>{Initialized, Stopped} </p></td>
- * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Prepared</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>prepareAsync </p></td>
- * <td>{Initialized, Stopped} </p></td>
- * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Preparing</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>release </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>After {@link #close()}, the object is no longer available. </p></td></tr>
- * <tr><td>reset </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted, Error}</p></td>
- * <td>{}</p></td>
- * <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
- * <tr><td>seekTo </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>setAudioAttributes </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio attributes type to become effective, this method must be called before
- * prepareAsync().</p></td></tr>
- * <tr><td>setAudioSessionId </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>This method must be called in idle state as the audio session ID must be known before
- * calling setDataSource or setPlaylist. Calling it does not change the object
- * state. </p></td></tr>
- * <tr><td>setAudioStreamType (deprecated)</p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio stream type to become effective, this method must be called before
- * prepareAsync().</p></td></tr>
- * <tr><td>setAuxEffectSendLevel </p></td>
- * <td>any</p></td>
- * <td>{} </p></td>
- * <td>Calling this method does not change the object state. </p></td></tr>
- * <tr><td>setDataSource </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Initialized</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setPlaylist </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Initialized</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setDisplay </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setSurface </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setVideoScalingMode </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>setLooping </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>isLooping </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>registerDrmEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>registerEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setPlaybackParams</p></td>
- * <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
- * <td>{Idle, Stopped} </p></td>
- * <td>This method will change state in some cases, depending on when it's called.
- * </p></td></tr>
- * <tr><td>setScreenOnWhilePlaying</></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setVolume </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state.
- * <tr><td>setWakeMode </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state.</p></td></tr>
- * <tr><td>start </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Started</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>stop </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Stopped</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>getTrackInfo </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>addTimedTextSource </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>selectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>deselectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- *
- * </table>
- *
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
- * android.R.styleable#AndroidManifestUsesPermission <uses-permission>}
- * element.
- *
- * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
- * when used with network-based content.
- *
- * <a name="Callbacks"></a>
- * <h3>Callbacks</h3>
- * <p>Applications may want to register for informational and error
- * events in order to be informed of some internal state update and
- * possible runtime errors during playback or streaming. Registration for
- * these events is done by properly setting the appropriate listeners (via calls
- * to
- * {@link #registerEventCallback(Executor, EventCallback)},
- * {@link #registerDrmEventCallback(Executor, DrmEventCallback)}).
- * In order to receive the respective callback
- * associated with these listeners, applications are required to create
- * MediaPlayer2 objects on a thread with its own Looper running (main UI
- * thread by default has a Looper running).
- *
* @hide
*/
public final class MediaPlayer2Impl extends MediaPlayer2 {
@@ -558,12 +110,13 @@
private final CloseGuard mGuard = CloseGuard.get();
private final Object mPlLock = new Object();
+ private DataSourceDesc mCurrentDSD;
+ private long mCurrentSrcId = 0;
private List<DataSourceDesc> mPlaylist;
- private int mPlCurrentIndex = 0;
+ private long mNextSrcId = mCurrentSrcId + 1;
private int mPlNextIndex = -1;
private int mPlNextSourceState = NEXT_SOURCE_STATE_INIT;
private boolean mPlNextSourcePlayPending = false;
- private int mLoopingMode = LOOPING_MODE_NONE;
// Modular DRM
private UUID mDrmUUID;
@@ -604,6 +157,381 @@
native_setup(new WeakReference<MediaPlayer2Impl>(this));
}
+ /**
+ * Releases the resources held by this {@code MediaPlayer2} object.
+ *
+ * It is considered good practice to call this method when you're
+ * done using the MediaPlayer2. In particular, whenever an Activity
+ * of an application is paused (its onPause() method is called),
+ * or stopped (its onStop() method is called), this method should be
+ * invoked to release the MediaPlayer2 object, unless the application
+ * has a special need to keep the object around. In addition to
+ * unnecessary resources (such as memory and instances of codecs)
+ * being held, failure to call this method immediately if a
+ * MediaPlayer2 object is no longer needed may also lead to
+ * continuous battery consumption for mobile devices, and playback
+ * failure for other applications if no multiple instances of the
+ * same codec are supported on a device. Even if multiple instances
+ * of the same codec are supported, some performance degradation
+ * may be expected when unnecessary multiple instances are used
+ * at the same time.
+ *
+ * {@code close()} may be safely called after a prior {@code close()}.
+ * This class implements the Java {@code AutoCloseable} interface and
+ * may be used with try-with-resources.
+ */
+ @Override
+ public void close() {
+ synchronized (mGuard) {
+ release();
+ }
+ }
+
+ /**
+ * Starts or resumes playback. If playback had previously been paused,
+ * playback will continue from where it was paused. If playback had
+ * been stopped, or never started before, playback will start at the
+ * beginning.
+ *
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ @Override
+ public void play() {
+ stayAwake(true);
+ _start();
+ }
+
+ private native void _start() throws IllegalStateException;
+
+ /**
+ * Prepares the player for playback, asynchronously.
+ *
+ * After setting the datasource and the display surface, you need to either
+ * call prepare(). For streams, you should call prepare(),
+ * which returns immediately, rather than blocking until enough data has been
+ * buffered.
+ *
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ @Override
+ public native void prepare();
+
+ /**
+ * Pauses playback. Call play() to resume.
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ */
+ @Override
+ public void pause() {
+ stayAwake(false);
+ _pause();
+ }
+
+ private native void _pause() throws IllegalStateException;
+
+ /**
+ * Tries to play next data source if applicable.
+ *
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ @Override
+ public void skipToNext() {
+ // TODO: switch to next data source and play
+ }
+
+ /**
+ * Gets the current playback position.
+ *
+ * @return the current position in milliseconds
+ */
+ @Override
+ public native long getCurrentPosition();
+
+ /**
+ * Gets the duration of the file.
+ *
+ * @return the duration in milliseconds, if no duration is available
+ * (for example, if streaming live content), -1 is returned.
+ */
+ @Override
+ public native long getDuration();
+
+ /**
+ * Gets the current buffered media source position received through progressive downloading.
+ * The received buffering percentage indicates how much of the content has been buffered
+ * or played. For example a buffering update of 80 percent when half the content
+ * has already been played indicates that the next 30 percent of the
+ * content to play has been buffered.
+ *
+ * @return the current buffered media source position in milliseconds
+ */
+ @Override
+ public long getBufferedPosition() {
+ // TODO: either get buffered position from native code, or cache BUFFERING_UPDATE
+ // number and convert it to buffered position.
+ return 0;
+ }
+
+ /**
+ * Gets the current player state.
+ *
+ * @return the current player state, one of the following:
+ * <ul>
+ * <li>{@link #PLAYER_STATE_IDLE}
+ * <li>{@link #PLAYER_STATE_PAUSED}
+ * <li>{@link #PLAYER_STATE_PLAYING}
+ * <li>{@link #PLAYER_STATE_ERROR}
+ * </ul>
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized or has been released.
+ */
+ @Override
+ public @PlayerState int getPlayerState() {
+ // TODO: use cached state or call native function.
+ return PLAYER_STATE_IDLE;
+ }
+
+ /**
+ * Gets the current buffering state of the player.
+ * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+ * buffered.
+ * @return the buffering state, one of the following:
+ * <ul>
+ * <li>{@link #BUFFERING_STATE_UNKNOWN}
+ * <li>{@link #BUFFERING_STATE_BUFFERING_AND_PLAYABLE}
+ * <li>{@link #BUFFERING_STATE_BUFFERING_AND_STARVED}
+ * <li>{@link #BUFFERING_STATE_BUFFERING_COMPLETE}
+ * </ul>
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized or has been released.
+ */
+ @Override
+ public @BuffState int getBufferingState() {
+ // TODO: use cached state or call native function.
+ return BUFFERING_STATE_UNKNOWN;
+ }
+
+ /**
+ * Sets the audio attributes for this MediaPlayer2.
+ * See {@link AudioAttributes} for how to build and configure an instance of this class.
+ * You must call this method before {@link #prepare()} in order
+ * for the audio attributes to become effective thereafter.
+ * @param attributes a non-null set of audio attributes
+ * @throws IllegalArgumentException if the attributes are null or invalid.
+ */
+ @Override
+ public void setAudioAttributes(@NonNull AudioAttributes attributes) {
+ if (attributes == null) {
+ final String msg = "Cannot set AudioAttributes to null";
+ throw new IllegalArgumentException(msg);
+ }
+ mUsage = attributes.getUsage();
+ mBypassInterruptionPolicy = (attributes.getAllFlags()
+ & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
+ Parcel pattributes = Parcel.obtain();
+ attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
+ setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
+ pattributes.recycle();
+ }
+
+ @Override
+ public @NonNull AudioAttributes getAudioAttributes() {
+ Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
+ AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
+ pattributes.recycle();
+ return attributes;
+ }
+
+ /**
+ * Sets the data source as described by a DataSourceDesc.
+ *
+ * @param dsd the descriptor of data source you want to play
+ * @throws IllegalStateException if it is called in an invalid state
+ * @throws NullPointerException if dsd is null
+ */
+ @Override
+ public void setDataSource(@NonNull DataSourceDesc dsd) {
+ Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+ synchronized (mPlLock) {
+ mCurrentDSD = dsd;
+ try {
+ handleDataSource(true /* isCurrent */, dsd);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a single data source as described by a DataSourceDesc which will be played
+ * after current data source is finished.
+ *
+ * @param dsd the descriptor of data source you want to play after current one
+ * @throws IllegalStateException if it is called in an invalid state
+ * @throws NullPointerException if dsd is null
+ */
+ @Override
+ public void setNextDataSource(@NonNull DataSourceDesc dsd) {
+ Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+
+ // TODO: save dsd in a list
+ }
+
+ /**
+ * Sets a list of data sources to be played sequentially after current data source is done.
+ *
+ * @param dsds the list of data sources you want to play after current one
+ * @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if dsds is null or empty, or contains null DataSourceDesc
+ */
+ @Override
+ public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+ // TODO: save the list.
+ /*
+ if (dsds == null || dsds.size() == 0) {
+ throw new IllegalArgumentException("data source list cannot be null or empty.");
+ }
+ HashSet ids = new HashSet(pl.size());
+ for (DataSourceDesc dsd : pl) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("DataSourceDesc in playlist cannot be null.");
+ }
+ if (ids.add(dsd.getId()) == false) {
+ throw new IllegalArgumentException("DataSourceDesc Id in playlist should be unique.");
+ }
+ }
+
+ if (startIndex < 0) {
+ startIndex = 0;
+ } else if (startIndex >= pl.size()) {
+ startIndex = pl.size() - 1;
+ }
+
+ synchronized (mPlLock) {
+ mPlaylist = Collections.synchronizedList(new ArrayList(pl));
+ handleDataSource(true, mPlaylist.get(startIndex));
+ // TODO: handle the preparation of next source in the playlist.
+ // It should be processed after current source is prepared.
+ mPlNextIndex = getNextIndex_l();
+ }
+ */
+ }
+
+ /**
+ * Gets the current data source as described by a DataSourceDesc.
+ *
+ * @return the current DataSourceDesc
+ */
+ @Override
+ public @NonNull DataSourceDesc getCurrentDataSource() {
+ synchronized (mPlLock) {
+ return mCurrentDSD;
+ }
+ }
+
+ /**
+ * Configures the player to loop on the current data source.
+ * @param loop true if the current data source is meant to loop.
+ */
+ @Override
+ public void loopCurrent(boolean loop) {
+ // TODO: set the looping mode, send notification
+ setLooping(loop);
+ }
+
+ private native void setLooping(boolean looping);
+
+ /**
+ * Sets the playback speed.
+ * A value of 1.0f is the default playback value.
+ * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+ * before using negative values.<br>
+ * After changing the playback speed, it is recommended to query the actual speed supported
+ * by the player, see {@link #getPlaybackSpeed()}.
+ * @param speed the desired playback speed
+ */
+ @Override
+ public void setPlaybackSpeed(float speed) {
+ // TODO: send notification
+ setPlaybackParams(getPlaybackParams().setSpeed(speed));
+ }
+
+ /**
+ * Returns the actual playback speed to be used by the player when playing.
+ * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+ * @return the actual playback speed
+ */
+ @Override
+ public float getPlaybackSpeed() {
+ return getPlaybackParams().getSpeed();
+ }
+
+ /**
+ * Indicates whether reverse playback is supported.
+ * Reverse playback is indicated by negative playback speeds, see
+ * {@link #setPlaybackSpeed(float)}.
+ * @return true if reverse playback is supported.
+ */
+ @Override
+ public boolean isReversePlaybackSupported() {
+ return false;
+ }
+
+ /**
+ * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+ * on the audio samples.
+ * Note that this volume is specific to the player, and is separate from stream volume
+ * used across the platform.<br>
+ * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+ * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+ * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+ */
+ @Override
+ public void setPlayerVolume(float volume) {
+ // send notification
+ _setVolume(volume, volume);
+ }
+
+ private native void _setVolume(float leftVolume, float rightVolume);
+
+ /**
+ * Returns the current volume of this player to this player.
+ * Note that it does not take into account the associated stream volume.
+ * @return the player volume.
+ */
+ @Override
+ public float getPlayerVolume() {
+ // TODO: get real volume
+ return 1.0f;
+ }
+
+ /**
+ * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
+ */
+ @Override
+ public float getMaxPlayerVolume() {
+ return 1.0f;
+ }
+
+ /**
+ * Adds a callback to be notified of events for this player.
+ * @param e the {@link Executor} to be used for the events.
+ * @param cb the callback to receive the events.
+ */
+ @Override
+ public void registerPlayerEventCallback(@NonNull Executor e,
+ @NonNull PlayerEventCallback cb) {
+ }
+
+ /**
+ * Removes a previously registered callback for player events
+ * @param cb the callback to remove
+ */
+ @Override
+ public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb) {
+ }
+
+
private static final int NEXT_SOURCE_STATE_ERROR = -1;
private static final int NEXT_SOURCE_STATE_INIT = 0;
private static final int NEXT_SOURCE_STATE_PREPARING = 1;
@@ -666,6 +594,11 @@
}
}
+ @Override
+ public void notifyWhenCommandLabelReached(Object label) {
+ // TODO: create an entry in command queue
+ }
+
/**
* Sets the {@link SurfaceHolder} to use for displaying the video
* portion of the media.
@@ -768,335 +701,6 @@
public void clearPendingCommands() {
}
- /**
- * Sets the data source as described by a DataSourceDesc.
- *
- * @param dsd the descriptor of data source you want to play
- * @throws IllegalStateException if it is called in an invalid state
- * @throws NullPointerException if dsd is null
- */
- @Override
- public void setDataSource(@NonNull DataSourceDesc dsd) throws IOException {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
- synchronized (mPlLock) {
- mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>(1));
- mPlaylist.add(dsd);
- mPlCurrentIndex = 0;
- mPlNextIndex = -1;
- handleDataSource(true /* isCurrent */, dsd);
- }
- }
-
- /**
- * Gets the current data source as described by a DataSourceDesc.
- *
- * @return the current DataSourceDesc
- */
- @Override
- public DataSourceDesc getCurrentDataSource() {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- return null;
- }
- return mPlaylist.get(mPlCurrentIndex);
- }
- }
-
- /**
- * Sets the play list.
- *
- * If startIndex falls outside play list range, it will be clamped to the nearest index
- * in the play list.
- *
- * @param pl the play list of data source you want to play
- * @param startIndex the index of the DataSourceDesc in the play list you want to play first
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if pl is null or empty, or pl contains null DataSourceDesc
- */
- @Override
- public void setPlaylist(@NonNull List<DataSourceDesc> pl, int startIndex)
- throws IOException {
- if (pl == null || pl.size() == 0) {
- throw new IllegalArgumentException("play list cannot be null or empty.");
- }
- HashSet ids = new HashSet(pl.size());
- for (DataSourceDesc dsd : pl) {
- if (dsd == null) {
- throw new IllegalArgumentException("DataSourceDesc in play list cannot be null.");
- }
- if (ids.add(dsd.getId()) == false) {
- throw new IllegalArgumentException("DataSourceDesc Id in play list should be unique.");
- }
- }
-
- if (startIndex < 0) {
- startIndex = 0;
- } else if (startIndex >= pl.size()) {
- startIndex = pl.size() - 1;
- }
-
- synchronized (mPlLock) {
- mPlaylist = Collections.synchronizedList(new ArrayList(pl));
- mPlCurrentIndex = startIndex;
- handleDataSource(true /* isCurrent */, mPlaylist.get(startIndex));
- // TODO: handle the preparation of next source in the play list.
- // It should be processed after current source is prepared.
- mPlNextIndex = getNextIndex_l();
- }
- }
-
- /**
- * Gets a copy of the play list.
- *
- * @return a copy of the play list used by {@link MediaPlayer2}
- */
- @Override
- public List<DataSourceDesc> getPlaylist() {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- return null;
- }
- return new ArrayList(mPlaylist);
- }
- }
-
- /**
- * Sets the index of current DataSourceDesc in the play list to be played.
- *
- * @param index the index of DataSourceDesc in the play list you want to play
- * @throws IllegalArgumentException if the play list is null
- * @throws NullPointerException if index is outside play list range
- */
- @Override
- public void setCurrentPlaylistItem(int index) {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- throw new IllegalArgumentException("play list has not been set yet.");
- }
- if (index < 0 || index >= mPlaylist.size()) {
- throw new IndexOutOfBoundsException("index is out of play list range.");
- }
-
- if (index == mPlCurrentIndex) {
- return;
- }
-
- // TODO: in playing state, stop current source and start to play source of index.
- mPlCurrentIndex = index;
- }
- }
-
- /**
- * Sets the index of next-to-be-played DataSourceDesc in the play list.
- *
- * @param index the index of next-to-be-played DataSourceDesc in the play list
- * @throws IllegalArgumentException if the play list is null
- * @throws NullPointerException if index is outside play list range
- */
- @Override
- public void setNextPlaylistItem(int index) {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- throw new IllegalArgumentException("play list has not been set yet.");
- }
- if (index < 0 || index >= mPlaylist.size()) {
- throw new IndexOutOfBoundsException("index is out of play list range.");
- }
-
- if (index == mPlNextIndex) {
- return;
- }
-
- // TODO: prepare the new next-to-be-played DataSourceDesc
- mPlNextIndex = index;
- }
- }
-
- /**
- * Gets the current index of play list.
- *
- * @return the index of the current DataSourceDesc in the play list
- */
- @Override
- public int getCurrentPlaylistItemIndex() {
- synchronized (mPlLock) {
- return mPlCurrentIndex;
- }
- }
-
- /**
- * Sets the looping mode of the play list.
- * The mode shall be one of {@link #LOOPING_MODE_NONE}, {@link #LOOPING_MODE_FULL},
- * {@link #LOOPING_MODE_SINGLE}, {@link #LOOPING_MODE_SHUFFLE}.
- *
- * @param mode the mode in which the play list will be played
- * @throws IllegalArgumentException if mode is not supported
- */
- @Override
- public void setLoopingMode(@LoopingMode int mode) {
- if (mode != LOOPING_MODE_NONE
- && mode != LOOPING_MODE_FULL
- && mode != LOOPING_MODE_SINGLE
- && mode != LOOPING_MODE_SHUFFLE) {
- throw new IllegalArgumentException("mode is not supported.");
- }
-
- synchronized (mPlLock) {
- mLoopingMode = mode;
- if (mPlaylist == null) {
- return;
- }
-
- // TODO: handle the new mode if necessary.
- }
- }
-
- /**
- * Gets the looping mode of play list.
- *
- * @return the looping mode of the play list
- */
- @Override
- public int getLoopingMode() {
- synchronized (mPlLock) {
- return mPlCurrentIndex;
- }
- }
-
- /**
- * Moves the DataSourceDesc at indexFrom in the play list to indexTo.
- *
- * @throws IllegalArgumentException if the play list is null
- * @throws IndexOutOfBoundsException if indexFrom or indexTo is outside play list range
- */
- @Override
- public void movePlaylistItem(int indexFrom, int indexTo) {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- throw new IllegalArgumentException("play list has not been set yet.");
- }
- // TODO: move the DataSourceDesc from indexFrom to indexTo.
- }
- }
-
- /**
- * Removes the DataSourceDesc at index in the play list.
- *
- * If index is same as the current index of the play list, current DataSourceDesc
- * will be stopped and playback moves to next source in the list.
- *
- * @return the removed DataSourceDesc at index in the play list
- * @throws IllegalArgumentException if the play list is null
- * @throws IndexOutOfBoundsException if index is outside play list range
- */
- @Override
- public DataSourceDesc removePlaylistItem(int index) {
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- throw new IllegalArgumentException("play list has not been set yet.");
- }
-
- DataSourceDesc oldDsd = mPlaylist.remove(index);
- // TODO: if index == mPlCurrentIndex, stop current source and move to next one.
- // if index == mPlNextIndex, prepare the new next-to-be-played source.
- return oldDsd;
- }
- }
-
- /**
- * Inserts the DataSourceDesc to the play list at position index.
- *
- * This will not change the DataSourceDesc currently being played.
- * If index is less than or equal to the current index of the play list,
- * the current index of the play list will be incremented correspondingly.
- *
- * @param index the index you want to add dsd to the play list
- * @param dsd the descriptor of data source you want to add to the play list
- * @throws IndexOutOfBoundsException if index is outside play list range
- * @throws NullPointerException if dsd is null
- */
- @Override
- public void addPlaylistItem(int index, DataSourceDesc dsd) {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-
- synchronized (mPlLock) {
- if (mPlaylist == null) {
- if (index == 0) {
- mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>());
- mPlaylist.add(dsd);
- mPlCurrentIndex = 0;
- return;
- }
- throw new IllegalArgumentException("index should be 0 for first DataSourceDesc.");
- }
-
- long id = dsd.getId();
- for (DataSourceDesc pldsd : mPlaylist) {
- if (id == pldsd.getId()) {
- throw new IllegalArgumentException("Id of dsd already exists in the play list.");
- }
- }
-
- mPlaylist.add(index, dsd);
- if (index <= mPlCurrentIndex) {
- ++mPlCurrentIndex;
- }
- }
- }
-
- /**
- * replaces the DataSourceDesc at index in the play list with given dsd.
- *
- * When index is same as the current index of the play list, the current source
- * will be stopped and the new source will be played, except that if new
- * and old source only differ on end position and current media position is
- * smaller then the new end position.
- *
- * This will not change the DataSourceDesc currently being played.
- * If index is less than or equal to the current index of the play list,
- * the current index of the play list will be incremented correspondingly.
- *
- * @param index the index you want to add dsd to the play list
- * @param dsd the descriptor of data source you want to add to the play list
- * @throws IndexOutOfBoundsException if index is outside play list range
- * @throws NullPointerException if dsd is null
- */
- @Override
- public DataSourceDesc editPlaylistItem(int index, DataSourceDesc dsd) {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
- Preconditions.checkNotNull(mPlaylist, "the play list cannot be null");
-
- long id = dsd.getId();
- synchronized (mPlLock) {
- for (int i = 0; i < mPlaylist.size(); ++i) {
- if (i == index) {
- continue;
- }
- if (id == mPlaylist.get(i).getId()) {
- throw new IllegalArgumentException(
- "Id of dsd already exists in the play list.");
- }
- }
-
- // TODO: if needed, stop playback of current source, and start new dsd.
- DataSourceDesc oldDsd = mPlaylist.set(index, dsd);
- return mPlaylist.set(index, dsd);
- }
- }
-
- // Called with mPlLock acquired.
- // TODO: support all looping modes
- private int getNextIndex_l() {
- if (mPlaylist.size() <= 1) {
- return -1;
- }
- int index = mPlCurrentIndex + 1;
- if (index >= mPlaylist.size()) {
- index = 0;
- }
- return index;
- }
-
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd)
throws IOException {
Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
@@ -1104,13 +708,13 @@
switch (dsd.getType()) {
case DataSourceDesc.TYPE_CALLBACK:
handleDataSource(isCurrent,
- dsd.getId(),
+ 0, // TODO: get mapped Id
dsd.getMedia2DataSource());
break;
case DataSourceDesc.TYPE_FD:
handleDataSource(isCurrent,
- dsd.getId(),
+ 0, // TODO: get mapped Id
dsd.getFileDescriptor(),
dsd.getFileDescriptorOffset(),
dsd.getFileDescriptorLength());
@@ -1118,7 +722,7 @@
case DataSourceDesc.TYPE_URI:
handleDataSource(isCurrent,
- dsd.getId(),
+ 0, // TODO: get mapped Id
dsd.getUriContext(),
dsd.getUri(),
dsd.getUriHeaders(),
@@ -1304,11 +908,11 @@
try {
mPlNextSourceState = NEXT_SOURCE_STATE_PREPARING;
- handleDataSource(false /* isCurrent */, mPlaylist.get(mPlNextIndex));
+ handleDataSource(false /* isCurrent */, mPlaylist.get(0));
} catch (Exception e) {
Message msg2 = mEventHandler.obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- final long nextSrcId = mPlaylist.get(mPlNextIndex).getId();
+ final long nextSrcId = mNextSrcId;
mEventHandler.post(new Runnable() {
@Override
public void run() {
@@ -1326,12 +930,12 @@
if (mPlNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
// Switch to next source only when it's in prepared state.
- mPlCurrentIndex = mPlNextIndex;
- mPlNextIndex = getNextIndex_l();
+ mCurrentSrcId = mNextSrcId;
+ mNextSrcId = 0; // TODO; fix it
mPlNextSourceState = NEXT_SOURCE_STATE_INIT;
mPlNextSourcePlayPending = false;
- long srcId = mPlaylist.get(mPlCurrentIndex).getId();
+ long srcId = mCurrentSrcId;
try {
nativePlayNextDataSource(srcId);
} catch (Exception e) {
@@ -1356,35 +960,6 @@
private native void nativePlayNextDataSource(long srcId);
- /**
- * Prepares the player for playback, asynchronously.
- *
- * After setting the datasource and the display surface, you need to either
- * call prepareAsync(). For streams, you should call prepareAsync(),
- * which returns immediately, rather than blocking until enough data has been
- * buffered.
- *
- * @throws IllegalStateException if it is called in an invalid state
- */
- @Override
- public native void prepareAsync();
-
- /**
- * Starts or resumes playback. If playback had previously been paused,
- * playback will continue from where it was paused. If playback had
- * been stopped, or never started before, playback will start at the
- * beginning.
- *
- * @throws IllegalStateException if it is called in an invalid state
- */
- @Override
- public void play() {
- stayAwake(true);
- _start();
- }
-
- private native void _start() throws IllegalStateException;
-
private int getAudioStreamType() {
if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -1410,20 +985,6 @@
private native void _stop() throws IllegalStateException;
- /**
- * Pauses playback. Call play() to resume.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
- @Override
- public void pause() {
- stayAwake(false);
- _pause();
- }
-
- private native void _pause() throws IllegalStateException;
-
//--------------------------------------------------------------------------
// Explicit Routing
//--------------------
@@ -1634,9 +1195,10 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #registerEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the width is available.
+ * yet. The {@code MediaPlayer2EventCallback} can be registered via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+ * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * is available.
*/
@Override
public native int getVideoWidth();
@@ -1646,9 +1208,10 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #registerEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the height is available.
+ * yet. The {@code MediaPlayer2EventCallback} can be registered via
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+ * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height
+ * is available.
*/
@Override
public native int getVideoHeight();
@@ -1677,11 +1240,32 @@
* @return true if currently playing, false otherwise
* @throws IllegalStateException if the internal player engine has not been
* initialized or has been released.
+ * @hide
*/
@Override
public native boolean isPlaying();
/**
+ * Gets the current MediaPlayer2 state.
+ *
+ * @return the current MediaPlayer2 state, one of the following:
+ * <ul>
+ * <li>{@link #MEDIAPLAYER2_STATE_IDLE}
+ * <li>{@link #MEDIAPLAYER2_STATE_PREPARED}
+ * <li>{@link #MEDIAPLAYER2_STATE_PAUSED}
+ * <li>{@link #MEDIAPLAYER2_STATE_PLAYING}
+ * <li>{@link #MEDIAPLAYER2_STATE_ERROR}
+ * </ul>
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized or has been released.
+ */
+ @Override
+ public @MediaPlayer2State int getMediaPlayer2State() {
+ // TODO: get state from native layer or cached value.
+ return MEDIAPLAYER2_STATE_IDLE;
+ }
+
+ /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -1801,8 +1385,6 @@
@NonNull
public native SyncParams getSyncParams();
- private native final void _seekTo(long msec, int mode);
-
/**
* Moves the media to specified time position by considering the given mode.
* <p>
@@ -1850,6 +1432,8 @@
_seekTo(msec, mode);
}
+ private native final void _seekTo(long msec, int mode);
+
/**
* Get current playback position as a {@link MediaTimestamp}.
* <p>
@@ -1884,23 +1468,6 @@
}
/**
- * Gets the current playback position.
- *
- * @return the current position in milliseconds
- */
- @Override
- public native long getCurrentPosition();
-
- /**
- * Gets the duration of the file.
- *
- * @return the duration in milliseconds, if no duration is available
- * (for example, if streaming live content), -1 is returned.
- */
- @Override
- public native long getDuration();
-
- /**
* Gets the media metadata.
*
* @param update_only controls whether the full set of available
@@ -1988,7 +1555,7 @@
/**
* Resets the MediaPlayer2 to its uninitialized state. After calling
* this method, you will have to initialize it again by setting the
- * data source and calling prepareAsync().
+ * data source and calling prepare().
*/
@Override
public void reset() {
@@ -2061,45 +1628,6 @@
private native Parcel getParameter(int key);
- /**
- * Sets the audio attributes for this MediaPlayer2.
- * See {@link AudioAttributes} for how to build and configure an instance of this class.
- * You must call this method before {@link #prepareAsync()} in order
- * for the audio attributes to become effective thereafter.
- * @param attributes a non-null set of audio attributes
- * @throws IllegalArgumentException if the attributes are null or invalid.
- */
- @Override
- public void setAudioAttributes(AudioAttributes attributes) {
- if (attributes == null) {
- final String msg = "Cannot set AudioAttributes to null";
- throw new IllegalArgumentException(msg);
- }
- mUsage = attributes.getUsage();
- mBypassInterruptionPolicy = (attributes.getAllFlags()
- & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
- Parcel pattributes = Parcel.obtain();
- attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
- setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
- pattributes.recycle();
- }
-
- @Override
- public AudioAttributes getAudioAttributes() {
- Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
- AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
- pattributes.recycle();
- return attributes;
- }
-
- /**
- * Sets the player to be looping or non-looping.
- *
- * @param looping whether to loop or not
- * @hide
- */
- @Override
- public native void setLooping(boolean looping);
/**
* Checks whether the MediaPlayer2 is looping or non-looping.
@@ -2111,39 +1639,6 @@
public native boolean isLooping();
/**
- * Sets the volume on this player.
- * This API is recommended for balancing the output of audio streams
- * within an application. Unless you are writing an application to
- * control user settings, this API should be used in preference to
- * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
- * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
- * UI controls should be scaled logarithmically.
- *
- * @param leftVolume left volume scalar
- * @param rightVolume right volume scalar
- */
- /*
- * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
- * The single parameter form below is preferred if the channel volumes don't need
- * to be set independently.
- */
- @Override
- public void setVolume(float leftVolume, float rightVolume) {
- _setVolume(leftVolume, rightVolume);
- }
-
- private native void _setVolume(float leftVolume, float rightVolume);
-
- /**
- * Similar, excepts sets volume of all channels to same value.
- * @hide
- */
- @Override
- public void setVolume(float volume) {
- setVolume(volume, volume);
- }
-
- /**
* Sets the audio session ID.
*
* @param sessionId the audio session ID.
@@ -2189,7 +1684,6 @@
@Override
public native void attachAuxEffect(int effectId);
-
/**
* Sets the send level of the player to the attached auxiliary effect.
* See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
@@ -3029,36 +2523,6 @@
}
}
- /**
- * Releases the resources held by this {@code MediaPlayer2} object.
- *
- * It is considered good practice to call this method when you're
- * done using the MediaPlayer2. In particular, whenever an Activity
- * of an application is paused (its onPause() method is called),
- * or stopped (its onStop() method is called), this method should be
- * invoked to release the MediaPlayer2 object, unless the application
- * has a special need to keep the object around. In addition to
- * unnecessary resources (such as memory and instances of codecs)
- * being held, failure to call this method immediately if a
- * MediaPlayer2 object is no longer needed may also lead to
- * continuous battery consumption for mobile devices, and playback
- * failure for other applications if no multiple instances of the
- * same codec are supported on a device. Even if multiple instances
- * of the same codec are supported, some performance degradation
- * may be expected when unnecessary multiple instances are used
- * at the same time.
- *
- * {@code close()} may be safely called after a prior {@code close()}.
- * This class implements the Java {@code AutoCloseable} interface and
- * may be used with try-with-resources.
- */
- @Override
- public void close() {
- synchronized (mGuard) {
- release();
- }
- }
-
// Have to declare protected for finalize() since it is protected
// in the base class Object.
@Override
@@ -3161,23 +2625,28 @@
sendMessage(msg2);
}
+ final DataSourceDesc dsd;
synchronized (mPlLock) {
Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
- + ", currentIndex=" + mPlCurrentIndex + ", nextIndex=" + mPlNextIndex);
- if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+ + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+ if (srcId == mCurrentSrcId) {
prepareNextDataSource_l();
- } else if (mPlNextIndex >= 0 && srcId == mPlaylist.get(mPlNextIndex).getId()) {
+ dsd = mCurrentDSD;
+ } else if (mPlNextIndex >= 0 && srcId == mNextSrcId) {
mPlNextSourceState = NEXT_SOURCE_STATE_PREPARED;
if (mPlNextSourcePlayPending) {
playNextDataSource_l();
}
+ dsd = mPlaylist.get(0);
+ } else {
+ dsd = null;
}
}
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, srcId, MEDIA_INFO_PREPARED, 0));
+ mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
}
}
return;
@@ -3198,12 +2667,11 @@
}
// notifying the client outside the lock
- // TODO: get srcId
if (drmInfo != null) {
synchronized (mEventCbLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
cb.first.execute(() -> cb.second.onDrmInfo(
- mMediaPlayer, 0, drmInfo));
+ mMediaPlayer, mCurrentDSD, drmInfo));
}
}
}
@@ -3214,17 +2682,17 @@
case MEDIA_PLAYBACK_COMPLETE:
synchronized (mPlLock) {
- if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+ if (srcId == mCurrentSrcId) {
Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
- + ", currentIndex=" + mPlCurrentIndex + ", nextIndex=" + mPlNextIndex);
+ + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
playNextDataSource_l();
}
}
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, srcId, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, mCurrentDSD, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
}
stayAwake(false);
@@ -3252,18 +2720,18 @@
case MEDIA_BUFFERING_UPDATE:
final int percent = msg.arg1;
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onBufferingUpdate(
- mMediaPlayer, srcId, percent));
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE, percent));
}
}
return;
case MEDIA_SEEK_COMPLETE:
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, srcId, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onCallComplete(
+ mMediaPlayer, mCurrentDSD, MEDIA_CALL_SEEK_TO, 0));
}
}
// fall through
@@ -3281,9 +2749,9 @@
final int width = msg.arg1;
final int height = msg.arg2;
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onVideoSizeChanged(
- mMediaPlayer, srcId, width, height));
+ mMediaPlayer, mCurrentDSD, width, height));
}
}
return;
@@ -3291,11 +2759,11 @@
case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onError(
- mMediaPlayer, srcId, what, extra));
+ mMediaPlayer, mCurrentDSD, what, extra));
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, srcId, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, mCurrentDSD, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
}
stayAwake(false);
@@ -3304,7 +2772,7 @@
case MEDIA_INFO:
switch (msg.arg1) {
case MEDIA_INFO_STARTED_AS_NEXT:
- if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+ if (srcId == mCurrentSrcId) {
prepareNextDataSource_l();
}
break;
@@ -3342,9 +2810,9 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, srcId, what, extra));
+ mMediaPlayer, mCurrentDSD, what, extra));
}
}
// No real default action so far.
@@ -3368,8 +2836,8 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, srcId, text));
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, mCurrentDSD, text));
}
}
return;
@@ -3398,9 +2866,9 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
- mMediaPlayer, srcId, data));
+ mMediaPlayer, mCurrentDSD, data));
}
}
return;
@@ -3473,7 +2941,7 @@
case MEDIA_PREPARED:
// By this time, we've learned about DrmInfo's presence or absence. This is meant
- // mainly for prepareAsync() use case. For prepare(), this still can run to a race
+ // mainly for prepare() use case. For prepare(), this still can run to a race
// condition b/c MediaPlayerNative releases the prepare() lock before calling notify
// so we also set mDrmInfoResolved in prepare().
synchronized (mp.mDrmLock) {
@@ -3496,8 +2964,8 @@
}
private final Object mEventCbLock = new Object();
- private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords
- = new ArrayList<Pair<Executor, EventCallback> >();
+ private ArrayList<Pair<Executor, MediaPlayer2EventCallback> > mEventCallbackRecords
+ = new ArrayList<Pair<Executor, MediaPlayer2EventCallback> >();
/**
* Register a callback to be invoked when the media source is ready
@@ -3507,13 +2975,14 @@
* @param executor the executor through which the callback should be invoked
*/
@Override
- public void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback eventCallback) {
+ public void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull MediaPlayer2EventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null EventCallback");
+ throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
}
if (executor == null) {
- throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
+ throw new IllegalArgumentException(
+ "Illegal null Executor for the MediaPlayer2EventCallback");
}
synchronized (mEventCbLock) {
mEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -3521,17 +2990,13 @@
}
/**
- * Unregisters an {@link EventCallback}.
- *
- * @param callback an {@link EventCallback} to unregister
+ * Clears the {@link MediaPlayer2EventCallback}.
*/
@Override
- public void unregisterEventCallback(EventCallback callback) {
+ public void clearMediaPlayer2EventCallback() {
synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- if (cb.second == callback) {
- mEventCallbackRecords.remove(cb);
- }
+ for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ mEventCallbackRecords.remove(cb);
}
}
}
@@ -3583,13 +3048,14 @@
* @param executor the executor through which the callback should be invoked
*/
@Override
- public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+ public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull DrmEventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null EventCallback");
+ throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
}
if (executor == null) {
- throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
+ throw new IllegalArgumentException(
+ "Illegal null Executor for the MediaPlayer2EventCallback");
}
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -3597,18 +3063,13 @@
}
/**
- * Unregisters a {@link DrmEventCallback}.
- *
- * @param callback a {@link DrmEventCallback} to unregister
+ * Clears the {@link DrmEventCallback}.
*/
@Override
- public void unregisterDrmEventCallback(DrmEventCallback callback) {
+ public void clearDrmEventCallback() {
synchronized (mDrmEventCbLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- if (cb.second == callback) {
- mDrmEventCallbackRecords.remove(cb);
- break;
- }
+ mDrmEventCallbackRecords.remove(cb);
}
}
}
@@ -3617,7 +3078,7 @@
/**
* Retrieves the DRM Info associated with the current source
*
- * @throws IllegalStateException if called before prepareAsync()
+ * @throws IllegalStateException if called before prepare()
*/
@Override
public DrmInfo getDrmInfo() {
@@ -3668,7 +3129,7 @@
* @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 prepareAsync(), or the DRM was
+ * @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
@@ -3723,7 +3184,7 @@
try {
// only creating the DRM object to allow pre-openSession configuration
- prepareDrm_createDrmStep(uuid);
+ prepareDrm(uuid);
} catch (Exception e) {
Log.w(TAG, "prepareDrm(): Exception ", e);
mPrepareDrmInProgress = false;
@@ -3735,9 +3196,8 @@
// call the callback outside the lock
- // TODO: get srcId
if (mOnDrmConfigHelper != null) {
- mOnDrmConfigHelper.onDrmConfig(this, 0);
+ mOnDrmConfigHelper.onDrmConfig(this, mCurrentDSD);
}
synchronized (mDrmLock) {
@@ -3807,12 +3267,11 @@
// if finished successfully without provisioning, call the callback outside the lock
- // TODO: get srcId
if (allDoneWithoutProvisioning) {
synchronized (mDrmEventCbLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
cb.first.execute(() -> cb.second.onDrmPrepared(
- this, 0, PREPARE_DRM_STATUS_SUCCESS));
+ this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS));
}
}
}
@@ -3868,14 +3327,14 @@
* A key request/response exchange occurs between the app and a license server
* to obtain or release keys used to decrypt encrypted content.
* <p>
- * getKeyRequest() is used to obtain an opaque key request byte array that is
+ * getDrmKeyRequest() is used to obtain an opaque key request byte array that is
* delivered to the license server. The opaque key request byte array is returned
* in KeyRequest.data. The recommended URL to deliver the key request to is
* returned in KeyRequest.defaultUrl.
* <p>
* After the app has received the key request response from the server,
* it should deliver to the response to the DRM engine plugin using the method
- * {@link #provideKeyResponse}.
+ * {@link #provideDrmKeyResponse}.
*
* @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
@@ -3903,19 +3362,19 @@
*/
@Override
@NonNull
- public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+ public MediaDrm.KeyRequest getDrmKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
@Nullable String mimeType, @MediaDrm.KeyType int keyType,
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException
{
- Log.v(TAG, "getKeyRequest: " +
+ Log.v(TAG, "getDrmKeyRequest: " +
" keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
" keyType: " + keyType + " optionalParameters: " + optionalParameters);
synchronized (mDrmLock) {
if (!mActiveDrmScheme) {
- Log.e(TAG, "getKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeExceptionImpl("getKeyRequest: Has to set a DRM scheme first.");
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeExceptionImpl("getDrmKeyRequest: Has to set a DRM scheme first.");
}
try {
@@ -3930,16 +3389,16 @@
MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
keyType, hmapOptionalParameters);
- Log.v(TAG, "getKeyRequest: --> request: " + request);
+ Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
return request;
} catch (NotProvisionedException e) {
- Log.w(TAG, "getKeyRequest NotProvisionedException: " +
+ Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
"Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("getKeyRequest: Unexpected provisioning error.");
+ throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
} catch (Exception e) {
- Log.w(TAG, "getKeyRequest Exception " + e);
+ Log.w(TAG, "getDrmKeyRequest Exception " + e);
throw e;
}
@@ -3949,15 +3408,15 @@
/**
* A key response is received from the license server by the app, then it is
- * provided to the DRM engine plugin using provideKeyResponse. When the
+ * provided to the DRM engine plugin using provideDrmKeyResponse. When the
* response is for an offline key request, a key-set identifier is returned that
* can be used to later restore the keys to a new session with the method
- * {@ link # restoreKeys}.
+ * {@ link # restoreDrmKeys}.
* When the response is for a streaming or release request, null is returned.
*
* @param keySetId When the response is for a release request, keySetId identifies
* the saved key associated with the release request (i.e., the same keySetId
- * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the
+ * passed to the earlier {@ link #getDrmKeyRequest} call. It MUST be null when the
* response is for either streaming or offline key requests.
*
* @param response the byte array response from the server
@@ -3967,16 +3426,16 @@
* server rejected the request
*/
@Override
- public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
+ public byte[] provideDrmKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException
{
- Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response);
+ Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
synchronized (mDrmLock) {
if (!mActiveDrmScheme) {
- Log.e(TAG, "getKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeExceptionImpl("getKeyRequest: Has to set a DRM scheme first.");
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeExceptionImpl("getDrmKeyRequest: Has to set a DRM scheme first.");
}
try {
@@ -3986,19 +3445,19 @@
byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
- Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response +
+ Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response +
" --> " + keySetResult);
return keySetResult;
} catch (NotProvisionedException e) {
- Log.w(TAG, "provideKeyResponse NotProvisionedException: " +
+ Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
"Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("provideKeyResponse: " +
+ throw new IllegalStateException("provideDrmKeyResponse: " +
"Unexpected provisioning error.");
} catch (Exception e) {
- Log.w(TAG, "provideKeyResponse Exception " + e);
+ Log.w(TAG, "provideDrmKeyResponse Exception " + e);
throw e;
}
} // synchronized
@@ -4007,21 +3466,21 @@
/**
* Restore persisted offline keys into a new session. keySetId identifies the
- * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
+ * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
*
* @param keySetId identifies the saved key set to restore
*/
@Override
- public void restoreKeys(@NonNull byte[] keySetId)
+ public void restoreDrmKeys(@NonNull byte[] keySetId)
throws NoDrmSchemeException
{
- Log.v(TAG, "restoreKeys: keySetId: " + keySetId);
+ Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
synchronized (mDrmLock) {
if (!mActiveDrmScheme) {
- Log.w(TAG, "restoreKeys NoDrmSchemeException");
- throw new NoDrmSchemeExceptionImpl("restoreKeys: Has to set a DRM scheme first.");
+ Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+ throw new NoDrmSchemeExceptionImpl("restoreDrmKeys: Has to set a DRM scheme first.");
}
try {
@@ -4305,7 +3764,7 @@
// TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
// it anyway so it raises provisioning error if needed. We'd rather handle provisioning
- // at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse
+ // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
try {
mDrmSessionId = mDrmObj.openSession();
Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
@@ -4478,10 +3937,10 @@
} // synchronized
// calling the callback outside the lock
- // TODO: get srcId
synchronized (mDrmEventCbLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onDrmPrepared(mediaPlayer, 0, status));
+ cb.first.execute(() -> cb.second.onDrmPrepared(
+ mediaPlayer, mCurrentDSD, status));
}
}
} else { // blocking mode already has the lock
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index bc4ae7a..3739847 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -16,11 +16,9 @@
package android.media;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.media.MediaSession2.PlaylistParams;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -240,7 +238,7 @@
* gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
* @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
*/
- public abstract void setVolume(float volume);
+ public abstract void setPlayerVolume(float volume);
/**
* Returns the current volume of this player to this player.
@@ -250,7 +248,7 @@
public abstract float getPlayerVolume();
/**
- * @return the maximum volume that can be used in {@link #setVolume(float)}.
+ * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
*/
public float getMaxPlayerVolume() { return 1.0f; }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 7bfaeef..ae5a8c6 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayerBase.PlayerEventCallback;
+import android.media.MediaPlaylistController;
import android.media.session.MediaSession;
import android.media.session.MediaSession.Callback;
import android.media.session.PlaybackState;
@@ -69,7 +70,7 @@
* <p>
* When a session receive transport control commands, the session sends the commands directly to
* the the underlying media player set by {@link Builder} or
- * {@link #setPlayer(MediaPlayerBase)}.
+ * {@link #updatePlayer}.
* <p>
* When an app is finished performing playback it must call {@link #close()} to clean up the session
* and notify any controllers.
@@ -117,7 +118,7 @@
public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
/**
- * Command code for {@link MediaController2#skipToNext()} ()}.
+ * Command code for {@link MediaController2#skipToNext()}.
* <p>
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
@@ -125,7 +126,7 @@
public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
/**
- * Command code for {@link MediaController2#skipToPrevious()} ()}.
+ * Command code for {@link MediaController2#skipToPrevious()}.
* <p>
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
@@ -170,10 +171,10 @@
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
*/
- public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10;
+ public static final int COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM = 10;
/**
- * Command code for {@link MediaController2#setPlaylistParams(PlaylistParams)} ()}.
+ * Command code for {@link MediaController2#setPlaylistParams(PlaylistParams)}.
* <p>
* Command would be sent directly to the player if the session doesn't reject the request
* through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
@@ -347,12 +348,12 @@
.createMediaSession2Command(this, commandCode, null, null);
}
- public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extra) {
+ public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extras) {
if (action == null) {
throw new IllegalArgumentException("action shouldn't be null");
}
mProvider = ApiLoader.getProvider(context)
- .createMediaSession2Command(this, COMMAND_CODE_CUSTOM, action, extra);
+ .createMediaSession2Command(this, COMMAND_CODE_CUSTOM, action, extras);
}
public int getCommandCode() {
@@ -363,8 +364,8 @@
return mProvider.getCustomCommand_impl();
}
- public @Nullable Bundle getExtra() {
- return mProvider.getExtra_impl();
+ public @Nullable Bundle getExtras() {
+ return mProvider.getExtras_impl();
}
/**
@@ -433,6 +434,11 @@
return mProvider.hasCommand_impl(code);
}
+ public List<Command> getCommands() {
+ // TODO: implement this
+ return null;
+ }
+
/**
* @hide
*/
@@ -512,7 +518,7 @@
* @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
* @see #COMMAND_CODE_PLAYBACK_REWIND
* @see #COMMAND_CODE_PLAYBACK_SEEK_TO
- * @see #COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM
+ * @see #COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM
* @see #COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS
* @see #COMMAND_CODE_PLAYLIST_ADD
* @see #COMMAND_CODE_PLAYLIST_REMOVE
@@ -685,16 +691,38 @@
}
/**
- * Set volume provider to configure this session to use remote volume handling.
- * This must be called to receive volume button events, otherwise the system
- * will adjust the appropriate stream volume for this session's player.
+ * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
+ * to.
* <p>
- * Set {@code null} to reset.
*
- * @param volumeProvider The provider that will handle volume changes. Can be {@code null}.
+ * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
*/
- U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
- mProvider.setVolumeProvider_impl(volumeProvider);
+ U setPlayer(@NonNull MediaPlayerBase player) {
+ // TODO: Change the provider properly
+ mProvider.setPlayer_impl(player, null, null);
+ return (U) this;
+ }
+
+ /**
+ * Set the {@link MediaPlaylistController} for this session to manages playlist of the
+ * underlying {@link MediaPlayerBase player}.
+ *
+ * @param mplc a {@link MediaPlaylistController} that manages playlist of the
+ * {@code player.}
+ */
+ U setPlaylistController(@NonNull MediaPlaylistController mplc) {
+ // TODO: implement this
+ return (U) this;
+ }
+
+ /**
+ * Set the {@link VolumeProvider2} for this session to receive volume button events. If not
+ * set, system will adjust the appropriate stream volume for this session's player.
+ *
+ * @param volumeProvider The provider that will receive volume button events.
+ */
+ U setVolumeProvider(@NonNull VolumeProvider2 volumeProvider) {
+ // TODO: implement this
return (U) this;
}
@@ -759,23 +787,32 @@
// Override all methods just to show them with the type instead of generics in Javadoc.
// This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
- public Builder(Context context, @NonNull MediaPlayerBase player) {
+ public Builder(Context context) {
super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
- context, (Builder) instance, player));
- }
-
- public Builder(Context context, @NonNull MediaPlayerBase player,
- @NonNull MediaPlaylistController mplc) {
- //TODO use the MediaPlaylistController
- super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
- context, (Builder) instance, player));
- if (mplc == null) {
- throw new IllegalArgumentException("Illegal null PlaylistController");
- }
+ context, (Builder) instance));
}
@Override
- public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ public Builder setPlayer(@NonNull MediaPlayerBase player) {
+ if (player == null) {
+ throw new IllegalArgumentException("Illegal null MediaPlayerBase");
+ }
+ return super.setPlayer(player);
+ }
+
+ @Override
+ public Builder setPlaylistController(@NonNull MediaPlaylistController mplc) {
+ if (mplc == null) {
+ throw new IllegalArgumentException("Illegal null MediaPlaylistController");
+ }
+ return super.setPlaylistController(mplc);
+ }
+
+ @Override
+ public Builder setVolumeProvider(@NonNull VolumeProvider2 volumeProvider) {
+ if (volumeProvider == null) {
+ throw new IllegalArgumentException("Illegal null VolumeProvider2");
+ }
return super.setVolumeProvider(volumeProvider);
}
@@ -922,8 +959,8 @@
*
* @return
*/
- public @Nullable Bundle getExtra() {
- return mProvider.getExtra_impl();
+ public @Nullable Bundle getExtras() {
+ return mProvider.getExtras_impl();
}
/**
@@ -969,8 +1006,8 @@
return mProvider.setEnabled_impl(enabled);
}
- public Builder setExtra(Bundle extra) {
- return mProvider.setExtra_impl(extra);
+ public Builder setExtras(Bundle extras) {
+ return mProvider.setExtras_impl(extras);
}
public CommandButton build() {
@@ -1135,28 +1172,17 @@
* Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
* to. Events from the {@link MediaController2} will be sent directly to the underlying
* player on the {@link Handler} where the session is created on.
- * <p>
- * For the remote playback case which you want to handle volume by yourself, use
- * {@link #setPlayer(MediaPlayerBase, VolumeProvider2)}.
*
* @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
- * @throws IllegalArgumentException if the player is {@code null}.
+ * @param mplc a {@link MediaPlaylistController} that manages playlist of the
+ * {@code player.}
+ * @param volumeProvider The provider that will receive volume button events. If
+ * {@code null}, system will adjust the appropriate stream volume for this session's player.
*/
- public void setPlayer(@NonNull MediaPlayerBase player) {
- mProvider.setPlayer_impl(player);
- }
-
- /**
- * Set the underlying {@link MediaPlayerBase} with the volume provider for remote playback.
- *
- * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
- * @param volumeProvider a volume provider
- * @see #setPlayer(MediaPlayerBase)
- * @see Builder#setVolumeProvider(VolumeProvider2)
- */
- public void setPlayer(@NonNull MediaPlayerBase player,
- @NonNull VolumeProvider2 volumeProvider) {
- mProvider.setPlayer_impl(player, volumeProvider);
+ public void updatePlayer(@NonNull MediaPlayerBase player,
+ @Nullable MediaPlaylistController mplc, @NonNull VolumeProvider2 volumeProvider) {
+ // TODO: rename setPlayer_impl to updatePlayer_impl
+ mProvider.setPlayer_impl(player, mplc, volumeProvider);
}
@Override
@@ -1173,6 +1199,24 @@
}
/**
+ * @return playlist controller
+ */
+ public @Nullable
+ MediaPlaylistController getMediaPlaylistController() {
+ // TODO: implement this
+ return null;
+ }
+
+ /**
+ * @return volume provider
+ */
+ public @Nullable
+ VolumeProvider2 getVolumeProvider() {
+ // TODO: implement this
+ return null;
+ }
+
+ /**
* Returns the {@link SessionToken2} for creating {@link MediaController2}.
*/
public @NonNull
@@ -1185,24 +1229,13 @@
}
/**
- * Sets which type of audio focus will be requested during the playback, or configures playback
- * to not request audio focus. Valid values for focus requests are
- * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
- * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
- * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use
- * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be
- * requested when playback starts. You can for instance use this when playing a silent animation
- * through this class, and you don't want to affect other audio applications playing in the
- * background.
+ * Set the {@link AudioFocusRequest} to obtain the audio focus
*
- * @param focusGain the type of audio focus gain that will be requested, or
- * {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during
- * playback.
- * @hide
+ * @param afr the full request parameters
*/
- // TODO(jaewan): Revisit
- public void setAudioFocusRequest(int focusGain) {
- mProvider.setAudioFocusRequest_impl(focusGain);
+ public void setAudioFocusRequest(AudioFocusRequest afr) {
+ // TODO: implement this
+ // mProvider.setAudioFocusRequest_impl(focusGain);
}
/**
@@ -1368,9 +1401,9 @@
}
/**
- * Remove the media item at index in the play list.
+ * Remove the media item in the play list.
* <p>
- * If index is same as the current index of the playlist, current playback
+ * If the item is the currently playing item of the playlist, current playback
* will be stopped and playback moves to next source in the list.
*
* @throws IllegalArgumentException if the play list is null
@@ -1398,18 +1431,6 @@
}
/**
- * Edit the media item to the play list at position index. This is expected to be called when
- * the metadata information is updated.
- * <p>
- * This will not change the currently playing media item.
- *
- * @param item the media item you want to add to the play list
- */
- public void editPlaylistItem(@NonNull MediaItem2 item) {
- mProvider.editPlaylistItem_impl(item);
- }
-
- /**
* Replace the media item at index in the playlist.
* @param index the index of the item to replace
* @param item the new item
@@ -1462,10 +1483,10 @@
* Notify errors to the connected controllers
*
* @param errorCode error code
- * @param extra extra
+ * @param extras extras
*/
- public void notifyError(@ErrorCode int errorCode, int extra) {
- mProvider.notifyError_impl(errorCode, extra);
+ public void notifyError(@ErrorCode int errorCode, @Nullable Bundle extras) {
+ mProvider.notifyError_impl(errorCode, extras);
}
/**
@@ -1506,4 +1527,21 @@
public PlaybackState2 getPlaybackState() {
return mProvider.getPlaybackState_impl();
}
+
+ /**
+ * Get the playback speed.
+ *
+ * @return speed
+ */
+ public float getPlaybackSpeed() {
+ // TODO: implement this
+ return -1;
+ }
+
+ /**
+ * Set the playback speed.
+ */
+ public void setPlaybackSpeed(float speed) {
+ // TODO: implement this
+ }
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 209ec42..c0468dc9 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -40,7 +40,7 @@
* <p>
* For ways of retrieving {@link Ringtone} objects or to show a ringtone
* picker, see {@link RingtoneManager}.
- *
+ *
* @see RingtoneManager
*/
public class Ringtone {
@@ -97,7 +97,7 @@
/**
* Sets the stream type where this ringtone will be played.
- *
+ *
* @param streamType The stream, see {@link AudioManager}.
* @deprecated use {@link #setAudioAttributes(AudioAttributes)}
*/
@@ -111,7 +111,7 @@
/**
* Gets the stream type where this ringtone will be played.
- *
+ *
* @return The stream type, see {@link AudioManager}.
* @deprecated use of stream types is deprecated, see
* {@link #setAudioAttributes(AudioAttributes)}
@@ -146,9 +146,8 @@
}
/**
- * @hide
* Sets the player to be looping or non-looping.
- * @param looping whether to loop or not
+ * @param looping whether to loop or not.
*/
public void setLooping(boolean looping) {
synchronized (mPlaybackSettingsLock) {
@@ -158,7 +157,16 @@
}
/**
- * @hide
+ * Returns whether the looping mode was enabled on this player.
+ * @return true if this player loops when playing.
+ */
+ public boolean isLooping() {
+ synchronized (mPlaybackSettingsLock) {
+ return mIsLooping;
+ }
+ }
+
+ /**
* Sets the volume on this player.
* @param volume a raw scalar in range 0.0 to 1.0, where 0.0 mutes this player, and 1.0
* corresponds to no attenuation being applied.
@@ -173,6 +181,16 @@
}
/**
+ * Returns the volume scalar set on this player.
+ * @return a value between 0.0f and 1.0f.
+ */
+ public float getVolume() {
+ synchronized (mPlaybackSettingsLock) {
+ return mVolume;
+ }
+ }
+
+ /**
* Must be called synchronized on mPlaybackSettingsLock
*/
private void applyPlaybackProperties_sync() {
@@ -194,8 +212,8 @@
/**
* Returns a human-presentable title for ringtone. Looks in media
* content provider. If not in either, uses the filename
- *
- * @param context A context used for querying.
+ *
+ * @param context A context used for querying.
*/
public String getTitle(Context context) {
if (mTitle != null) return mTitle;
@@ -265,12 +283,11 @@
if (title == null) {
title = context.getString(com.android.internal.R.string.ringtone_unknown);
-
if (title == null) {
title = "";
}
}
-
+
return title;
}
@@ -395,7 +412,7 @@
/**
* Whether this ringtone is currently playing.
- *
+ *
* @return True if playing, false otherwise.
*/
public boolean isPlaying() {
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 8e1cfbf..711f51f 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -31,7 +31,7 @@
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
* <p>
* You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerBase, VolumeProvider2)}.
+ * {@link MediaSession2#updatePlayer}.
*/
// New version of VolumeProvider with following changes
// - Don't implement Parcelable for updatable support.
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index adeb834..39cdcf0 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -163,6 +163,19 @@
/** @hide */
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final AudioMix that = (AudioMix) o;
+ return (this.mRouteFlags == that.mRouteFlags)
+ && (this.mRule == that.mRule)
+ && (this.mMixType == that.mMixType)
+ && (this.mFormat == that.mFormat);
+ }
+
+ /** @hide */
+ @Override
public int hashCode() {
return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
}
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 5f12742..866b574 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -135,11 +135,31 @@
}
}
+ private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
+ ArrayList<AudioMixMatchCriterion> cr2) {
+ if (cr1 == null || cr2 == null) return false;
+ if (cr1 == cr2) return true;
+ if (cr1.size() != cr2.size()) return false;
+ //TODO iterate over rules to check they contain the same criterion
+ return (cr1.hashCode() == cr2.hashCode());
+ }
+
private final int mTargetMixType;
int getTargetMixType() { return mTargetMixType; }
private final ArrayList<AudioMixMatchCriterion> mCriteria;
ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final AudioMixingRule that = (AudioMixingRule) o;
+ return (this.mTargetMixType == that.mTargetMixType)
+ && (areCriteriaEquivalent(this.mCriteria, that.mCriteria));
+ }
+
@Override
public int hashCode() {
return Objects.hash(mTargetMixType, mCriteria);
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 343bbda..11107e2 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -42,6 +42,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
/**
* @hide
@@ -256,6 +257,89 @@
}
}
+ /**
+ * @hide
+ * Update the current configuration of the set of audio mixes by adding new ones, while
+ * keeping the policy registered.
+ * This method can only be called on a registered policy.
+ * @param mixes the list of {@link AudioMix} to add
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int attachMixes(@NonNull List<AudioMix> mixes) {
+ if (mixes == null) {
+ throw new IllegalArgumentException("Illegal null list of AudioMix");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+ }
+ final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+ for (AudioMix mix : mixes) {
+ if (mix == null) {
+ throw new IllegalArgumentException("Illegal null AudioMix in attachMixes");
+ } else {
+ zeMixes.add(mix);
+ }
+ }
+ final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+ IAudioService service = getService();
+ try {
+ final int status = service.addMixForPolicy(cfg, this.cb());
+ if (status == AudioManager.SUCCESS) {
+ mConfig.add(zeMixes);
+ }
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in attachMixes", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Update the current configuration of the set of audio mixes by removing some, while
+ * keeping the policy registered.
+ * This method can only be called on a registered policy.
+ * @param mixes the list of {@link AudioMix} to remove
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int detachMixes(@NonNull List<AudioMix> mixes) {
+ if (mixes == null) {
+ throw new IllegalArgumentException("Illegal null list of AudioMix");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+ }
+ final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+ for (AudioMix mix : mixes) {
+ if (mix == null) {
+ throw new IllegalArgumentException("Illegal null AudioMix in detachMixes");
+ // TODO also check mix is currently contained in list of mixes
+ } else {
+ zeMixes.add(mix);
+ }
+ }
+ final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+ IAudioService service = getService();
+ try {
+ final int status = service.removeMixForPolicy(cfg, this.cb());
+ if (status == AudioManager.SUCCESS) {
+ mConfig.remove(zeMixes);
+ }
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in detachMixes", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
public void setRegistration(String regId) {
synchronized (mLock) {
mRegistrationId = regId;
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index cafa5a8..f725cac 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -16,6 +16,7 @@
package android.media.audiopolicy;
+import android.annotation.NonNull;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioPatch;
@@ -24,6 +25,8 @@
import android.os.Parcelable;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Objects;
@@ -35,11 +38,16 @@
private static final String TAG = "AudioPolicyConfig";
- protected ArrayList<AudioMix> mMixes;
+ protected final ArrayList<AudioMix> mMixes;
protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;
private String mRegistrationId = null;
+ /** counter for the mixes that are / have been in the list of AudioMix
+ * e.g. register 4 mixes (counter is 3), remove 1 (counter is 3), add 1 (counter is 4)
+ */
+ private int mMixCounter = 0;
+
protected AudioPolicyConfig(AudioPolicyConfig conf) {
mMixes = conf.mMixes;
}
@@ -201,20 +209,39 @@
return;
}
mRegistrationId = regId == null ? "" : regId;
- int mixIndex = 0;
for (AudioMix mix : mMixes) {
- if (!mRegistrationId.isEmpty()) {
- if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
- AudioMix.ROUTE_FLAG_LOOP_BACK) {
- mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
- + mixIndex++);
- } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
- AudioMix.ROUTE_FLAG_RENDER) {
- mix.setRegistration(mix.mDeviceAddress);
- }
- } else {
- mix.setRegistration("");
+ setMixRegistration(mix);
+ }
+ }
+
+ private void setMixRegistration(@NonNull final AudioMix mix) {
+ if (!mRegistrationId.isEmpty()) {
+ if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
+ AudioMix.ROUTE_FLAG_LOOP_BACK) {
+ mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
+ + mMixCounter);
+ } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
+ AudioMix.ROUTE_FLAG_RENDER) {
+ mix.setRegistration(mix.mDeviceAddress);
}
+ } else {
+ mix.setRegistration("");
+ }
+ mMixCounter++;
+ }
+
+ @GuardedBy("mMixes")
+ protected void add(@NonNull ArrayList<AudioMix> mixes) {
+ for (AudioMix mix : mixes) {
+ setMixRegistration(mix);
+ mMixes.add(mix);
+ }
+ }
+
+ @GuardedBy("mMixes")
+ protected void remove(@NonNull ArrayList<AudioMix> mixes) {
+ for (AudioMix mix : mixes) {
+ mMixes.remove(mix);
}
}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 6d1b1b1..8d036be 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -59,9 +59,9 @@
void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
List<MediaItem2> getPlaylist_impl();
- void removePlaylistItem_impl(MediaItem2 index);
void addPlaylistItem_impl(int index, MediaItem2 item);
void replacePlaylistItem_impl(int index, MediaItem2 item);
+ void removePlaylistItem_impl(MediaItem2 item);
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
index 1d5b414..b494f9e 100644
--- a/media/java/android/media/update/MediaItem2Provider.java
+++ b/media/java/android/media/update/MediaItem2Provider.java
@@ -17,6 +17,8 @@
package android.media.update;
import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaItem2.Builder;
import android.media.MediaMetadata2;
import android.os.Bundle;
@@ -33,4 +35,11 @@
MediaMetadata2 getMetadata_impl();
String getMediaId_impl();
DataSourceDesc getDataSourceDesc_impl();
+
+ interface BuilderProvider {
+ Builder setMediaId_impl(String mediaId);
+ Builder setMetadata_impl(MediaMetadata2 metadata);
+ Builder setDataSourceDesc_impl(DataSourceDesc dataSourceDesc);
+ MediaItem2 build_impl();
+ }
}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index d0b11b7..d0ec104 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -22,6 +22,7 @@
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
import android.media.MediaPlayerBase.PlayerEventCallback;
+import android.media.MediaPlaylistController;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -43,8 +44,8 @@
*/
public interface MediaSession2Provider extends TransportControlProvider {
void close_impl();
- void setPlayer_impl(MediaPlayerBase player);
- void setPlayer_impl(MediaPlayerBase player, VolumeProvider2 volumeProvider);
+ void setPlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+ VolumeProvider2 volumeProvider);
MediaPlayerBase getPlayer_impl();
SessionToken2 getToken_impl();
List<ControllerInfo> getConnectedControllers_impl();
@@ -63,14 +64,14 @@
MediaItem2 getCurrentPlaylistItem_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaylistParams getPlaylistParams_impl();
- void notifyError_impl(int errorCode, int extra);
+ void notifyError_impl(int errorCode, Bundle extras);
void registerPlayerEventCallback_impl(Executor executor, PlayerEventCallback callback);
void unregisterPlayerEventCallback_impl(PlayerEventCallback callback);
interface CommandProvider {
int getCommandCode_impl();
String getCustomCommand_impl();
- Bundle getExtra_impl();
+ Bundle getExtras_impl();
Bundle toBundle_impl();
boolean equals_impl(Object ob);
@@ -90,7 +91,7 @@
Command getCommand_impl();
int getIconResId_impl();
String getDisplayName_impl();
- Bundle getExtra_impl();
+ Bundle getExtras_impl();
boolean isEnabled_impl();
interface BuilderProvider {
@@ -98,7 +99,7 @@
Builder setIconResId_impl(int resId);
Builder setDisplayName_impl(String displayName);
Builder setEnabled_impl(boolean enabled);
- Builder setExtra_impl(Bundle extra);
+ Builder setExtras_impl(Bundle extras);
CommandButton build_impl();
}
}
@@ -119,7 +120,8 @@
}
interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
- void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
+ void setPlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+ VolumeProvider2 volumeProvider);
void setSessionActivity_impl(PendingIntent pi);
void setId_impl(String id);
void setSessionCallback_impl(Executor executor, C callback);
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 62759eb..843fa71 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -30,7 +30,6 @@
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerBase;
import android.media.MediaSession2;
import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.PlaylistParams;
@@ -86,9 +85,9 @@
MediaMetadata2 playlistMetadata);
PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
- MediaSession2.CommandButton.Builder builder);
+ MediaSession2.CommandButton.Builder instance);
BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
- Context context, MediaSession2.Builder instance, MediaPlayerBase player);
+ Context context, MediaSession2.Builder instance);
MediaController2Provider createMediaController2(Context context, MediaController2 instance,
SessionToken2 token, Executor executor, ControllerCallback callback);
@@ -104,8 +103,7 @@
BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
createMediaLibraryService2Builder(
MediaLibraryService2 service, MediaLibrarySession.Builder instance,
- MediaPlayerBase player, Executor callbackExecutor,
- MediaLibrarySessionCallback callback);
+ Executor callbackExecutor, MediaLibrarySessionCallback callback);
LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
String rootId, Bundle extras);
@@ -113,8 +111,8 @@
String packageName, String serviceName, int uid);
SessionToken2 fromBundle_SessionToken2(Context context, Bundle bundle);
- MediaItem2Provider createMediaItem2(Context context, MediaItem2 mediaItem2,
- String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags);
+ MediaItem2Provider.BuilderProvider createMediaItem2Builder(
+ Context context, MediaItem2.Builder instance, int flags);
MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle);
VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
@@ -122,9 +120,9 @@
MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle);
MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- Context context, MediaMetadata2.Builder builder);
+ Context context, MediaMetadata2.Builder instance);
MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- Context context, MediaMetadata2.Builder builder, MediaMetadata2 source);
+ Context context, MediaMetadata2.Builder instance, MediaMetadata2 source);
Rating2 newUnratedRating_Rating2(Context context, int ratingStyle);
Rating2 fromBundle_Rating2(Context context, Bundle bundle);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3c8af8a..44e5d61 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,6 +31,7 @@
shared_libs: [
"libandroid_runtime",
"libnativehelper",
+ "libnativewindow",
"libutils",
"libbinder",
"libmedia",
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 885bf03..f5311764 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -33,11 +33,14 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <grallocusage/GrallocUsageConversion.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <stdint.h>
#include <inttypes.h>
+#include <android/hardware_buffer_jni.h>
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
@@ -797,6 +800,14 @@
}
}
+static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
+ AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
+ // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
+ // to link against libandroid.so
+ return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
+}
+
} // extern "C"
// ----------------------------------------------------------------------------
@@ -814,10 +825,12 @@
static const JNINativeMethod gImageMethods[] = {
{"nativeCreatePlanes", "(II)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
- (void*)Image_createSurfacePlanes },
- {"nativeGetWidth", "()I", (void*)Image_getWidth },
- {"nativeGetHeight", "()I", (void*)Image_getHeight },
- {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
+ (void*)Image_createSurfacePlanes },
+ {"nativeGetWidth", "()I", (void*)Image_getWidth },
+ {"nativeGetHeight", "()I", (void*)Image_getHeight },
+ {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
+ {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
+ (void*)Image_getHardwareBuffer },
};
int register_android_media_ImageReader(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 62030bb..a1022c0 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1011,7 +1011,7 @@
sp<IDescrambler> descrambler;
if (descramblerBinderObj != NULL) {
- descrambler = JDescrambler::GetDescrambler(env, descramblerBinderObj);
+ descrambler = GetDescrambler(env, descramblerBinderObj);
}
err = codec->configure(format, bufferProducer, crypto, descrambler, flags);
diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp
index add47463..aa79ce0 100644
--- a/media/jni/android_media_MediaDescrambler.cpp
+++ b/media/jni/android_media_MediaDescrambler.cpp
@@ -25,18 +25,64 @@
#include <android/hardware/cas/native/1.0/BpHwDescrambler.h>
#include <android/hardware/cas/native/1.0/BnHwDescrambler.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <binder/MemoryDealer.h>
#include <hidl/HidlSupport.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/cas/DescramblerAPI.h>
#include <nativehelper/ScopedLocalRef.h>
namespace android {
+class IMemory;
+class MemoryDealer;
+namespace hardware {
+class HidlMemory;
+};
using hardware::fromHeap;
+using hardware::HidlMemory;
+using hardware::hidl_string;
+using hardware::hidl_vec;
+using namespace hardware::cas::V1_0;
+using namespace hardware::cas::native::V1_0;
+
+struct JDescrambler : public RefBase {
+ JDescrambler(JNIEnv *env, jobject descramberBinderObj);
+
+ status_t descramble(
+ uint32_t key,
+ ssize_t totalLength,
+ const hidl_vec<SubSample>& subSamples,
+ const void *srcPtr,
+ jint srcOffset,
+ void *dstPtr,
+ jint dstOffset,
+ Status *status,
+ uint32_t *bytesWritten,
+ hidl_string *detailedError);
+
+
+protected:
+ virtual ~JDescrambler();
+
+private:
+ sp<IDescrambler> mDescrambler;
+ sp<IMemory> mMem;
+ sp<MemoryDealer> mDealer;
+ sp<HidlMemory> mHidlMemory;
+ SharedBuffer mDescramblerSrcBuffer;
+
+ Mutex mSharedMemLock;
+
+ bool ensureBufferCapacity(size_t neededSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JDescrambler);
+};
struct fields_t {
jfieldID context;
+ jbyte flagPesHeader;
};
static fields_t gFields;
@@ -111,8 +157,7 @@
mDealer.clear();
}
-// static
-sp<IDescrambler> JDescrambler::GetDescrambler(JNIEnv *env, jobject obj) {
+sp<IDescrambler> GetDescrambler(JNIEnv *env, jobject obj) {
if (obj != NULL) {
sp<hardware::IBinder> hwBinder =
JHwRemoteBinder::GetNativeContext(env, obj)->getBinder();
@@ -155,7 +200,7 @@
}
status_t JDescrambler::descramble(
- jbyte key,
+ uint32_t key,
ssize_t totalLength,
const hidl_vec<SubSample>& subSamples,
const void *srcPtr,
@@ -228,6 +273,12 @@
gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
CHECK(gFields.context != NULL);
+
+ jfieldID fieldPesHeader = env->GetStaticFieldID(
+ clazz.get(), "SCRAMBLE_FLAG_PES_HEADER", "B");
+ CHECK(fieldPesHeader != NULL);
+
+ gFields.flagPesHeader = env->GetStaticByteField(clazz.get(), fieldPesHeader);
}
static void android_media_MediaDescrambler_native_setup(
@@ -323,7 +374,7 @@
}
static jint android_media_MediaDescrambler_native_descramble(
- JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
+ JNIEnv *env, jobject thiz, jbyte key, jbyte flags, jint numSubSamples,
jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
jobject srcBuf, jint srcOffset, jint srcLimit,
jobject dstBuf, jint dstOffset, jint dstLimit) {
@@ -364,12 +415,18 @@
return -1;
}
+ uint32_t scramblingControl = (uint32_t)key;
+
+ if (flags & gFields.flagPesHeader) {
+ scramblingControl |= DescramblerPlugin::kScrambling_Flag_PesHeader;
+ }
+
Status status;
uint32_t bytesWritten;
hidl_string detailedError;
err = descrambler->descramble(
- key, totalLength, subSamples,
+ scramblingControl, totalLength, subSamples,
srcPtr, srcOffset, dstPtr, dstOffset,
&status, &bytesWritten, &detailedError);
@@ -401,7 +458,7 @@
(void *)android_media_MediaDescrambler_native_init },
{ "native_setup", "(Landroid/os/IHwBinder;)V",
(void *)android_media_MediaDescrambler_native_setup },
- { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
+ { "native_descramble", "(BBI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
(void *)android_media_MediaDescrambler_native_descramble },
};
diff --git a/media/jni/android_media_MediaDescrambler.h b/media/jni/android_media_MediaDescrambler.h
index 2354dc2..0ae4187 100644
--- a/media/jni/android_media_MediaDescrambler.h
+++ b/media/jni/android_media_MediaDescrambler.h
@@ -19,57 +19,19 @@
#include "jni.h"
-#include <android/hardware/cas/native/1.0/IDescrambler.h>
-
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/Mutex.h>
+#include <utils/RefBase.h>
namespace android {
-class IMemory;
-class MemoryDealer;
namespace hardware {
-class HidlMemory;
-};
-using hardware::HidlMemory;
-using hardware::hidl_string;
-using hardware::hidl_vec;
-using namespace hardware::cas::V1_0;
-using namespace hardware::cas::native::V1_0;
+namespace cas {
+namespace native {
+namespace V1_0 {
+struct IDescrambler;
+}}}}
+using hardware::cas::native::V1_0::IDescrambler;
-struct JDescrambler : public RefBase {
- JDescrambler(JNIEnv *env, jobject descramberBinderObj);
-
- status_t descramble(
- jbyte key,
- ssize_t totalLength,
- const hidl_vec<SubSample>& subSamples,
- const void *srcPtr,
- jint srcOffset,
- void *dstPtr,
- jint dstOffset,
- Status *status,
- uint32_t *bytesWritten,
- hidl_string *detailedError);
-
- static sp<IDescrambler> GetDescrambler(JNIEnv *env, jobject obj);
-
-protected:
- virtual ~JDescrambler();
-
-private:
- sp<IDescrambler> mDescrambler;
- sp<IMemory> mMem;
- sp<MemoryDealer> mDealer;
- sp<HidlMemory> mHidlMemory;
- SharedBuffer mDescramblerSrcBuffer;
-
- Mutex mSharedMemLock;
-
- bool ensureBufferCapacity(size_t neededSize);
-
- DISALLOW_EVIL_CONSTRUCTORS(JDescrambler);
-};
+sp<IDescrambler> GetDescrambler(JNIEnv *env, jobject obj);
} // namespace android
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 2258c78..b0936fb 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -471,7 +471,7 @@
// This will fail if the media player has not been initialized yet. This
// can be the case if setDisplay() on MediaPlayer2Impl.java has been called
// before setDataSource(). The redundant call to setVideoSurfaceTexture()
- // in prepare/prepareAsync covers for this case.
+ // in prepare/prepare covers for this case.
mp->setVideoSurfaceTexture(new ANativeWindowWrapper(anw));
}
@@ -536,7 +536,7 @@
}
static void
-android_media_MediaPlayer2_prepareAsync(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_prepare(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -1480,7 +1480,7 @@
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface},
{"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
{"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
- {"prepareAsync", "()V", (void *)android_media_MediaPlayer2_prepareAsync},
+ {"prepare", "()V", (void *)android_media_MediaPlayer2_prepare},
{"_start", "()V", (void *)android_media_MediaPlayer2_start},
{"_stop", "()V", (void *)android_media_MediaPlayer2_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer2_getVideoWidth},
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
index e88c0f1..63f9f91 100644
--- a/media/lib/remotedisplay/Android.mk
+++ b/media/lib/remotedisplay/Android.mk
@@ -42,3 +42,24 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.media.remotedisplay.stubs-gen
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.media.remotedisplay.stubs_intermediates/src
+LOCAL_DROIDDOC_OPTIONS:= \
+ -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
+ -stubpackages com.android.media.remotedisplay \
+ -nodocs
+LOCAL_UNINSTALLABLE_MODULE := true
+include $(BUILD_DROIDDOC)
+com_android_media_remotedisplay_gen_stamp := $(full_target)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.media.remotedisplay.stubs
+LOCAL_SDK_VERSION := current
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_media_remotedisplay_gen_stamp)
+com_android_media_remotedisplay_gen_stamp :=
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media/lib/remotedisplay/README.txt b/media/lib/remotedisplay/README.txt
index 5738dbe..1a52c4d 100644
--- a/media/lib/remotedisplay/README.txt
+++ b/media/lib/remotedisplay/README.txt
@@ -1,8 +1,17 @@
-This library (com.android.media.remotedisplay.jar) is a shared java library
+There are two libraries defined in this directory:
+First, com.android.media.remotedisplay.jar is a shared java library
containing classes required by unbundled remote display providers.
+Second, com.android.media.remotedisplay.stubs.jar is a stub for the shared
+library which provides build-time APIs to the unbundled clients.
+
+At runtime, the shared library is added to the classloader of the app via the
+<uses-library> tag. And since Java always tries to load a class from the
+parent classloader, regardless of whether the stub library is linked to the
+app statically or dynamically, the real classes are loaded from the shared
+library.
--- Rules of this library ---
-o This library is effectively a PUBLIC API for unbundled remote display providers
+o The stub library is effectively a PUBLIC API for unbundled remote display providers
that may be distributed outside the system image. So it MUST BE API STABLE.
You can add but not remove. The rules are the same as for the
public platform SDK API.
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index 69ca4d2..54aa968 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -42,3 +42,24 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.mediadrm.signer.stubs-gen
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.mediadrm.signer.stubs_intermediates/src
+LOCAL_DROIDDOC_OPTIONS:= \
+ -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
+ -stubpackages com.android.mediadrm.signer \
+ -nodocs
+LOCAL_UNINSTALLABLE_MODULE := true
+include $(BUILD_DROIDDOC)
+com_android_mediadrm_signer_gen_stamp := $(full_target)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.mediadrm.signer.stubs
+LOCAL_SDK_VERSION := current
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_mediadrm_signer_gen_stamp)
+com_android_mediadrm_signer_gen_stamp :=
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media/lib/signer/README.txt b/media/lib/signer/README.txt
index 362ab8e..55df3fc 100644
--- a/media/lib/signer/README.txt
+++ b/media/lib/signer/README.txt
@@ -1,10 +1,19 @@
-This library (com.android.mediadrm.signer.jar) is a shared java library
+There are two libraries defined in this directory:
+First, com.android.mediadrm.signer.jar is a shared java library
containing classes required by unbundled apps running on devices that use
the certficate provisioning and private key signing capabilities provided
by the MediaDrm API.
+Second, com.android.mediadrm.signer.stubs.jar is a stub for the shared library
+which provides build-time APIs to the unbundled clients.
+
+At runtime, the shared library is added to the classloader of the app via the
+<uses-library> tag. And since Java always tries to load a class from the
+parent classloader, regardless of whether the stub library is linked to the
+app statically or dynamically, the real classes are loaded from the shared
+library.
--- Rules of this library ---
-o This library is effectively a PUBLIC API for unbundled CAST receivers
+o The stub library is effectively a PUBLIC API for unbundled CAST receivers
that may be distributed outside the system image. So it MUST BE API STABLE.
You can add but not remove. The rules are the same as for the
public platform SDK API.
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 7e23ee1..16ef59f 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -41,7 +41,8 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "DeviceChooserActivity";
- private ListView mDeviceListView;
+ View mLoadingIndicator = null;
+ ListView mDeviceListView;
private View mPairButton;
private View mCancelButton;
@@ -80,8 +81,9 @@
onSelectionUpdate();
}
});
- mDeviceListView.addFooterView(getProgressBar(), null, false);
+ mDeviceListView.addFooterView(mLoadingIndicator = getProgressBar(), null, false);
}
+ getService().mActivity = this;
mCancelButton = findViewById(R.id.button_cancel);
mCancelButton.setOnClickListener(v -> cancel());
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 1e26231..a5f0f24 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -22,6 +22,7 @@
import static com.android.internal.util.ArrayUtils.isEmpty;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.CollectionUtils.size;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,6 +51,7 @@
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.wifi.WifiManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -63,7 +65,9 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -73,6 +77,8 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "DeviceDiscoveryService";
+ private static final long SCAN_TIMEOUT = 20000;
+
static DeviceDiscoveryService sInstance;
private BluetoothAdapter mBluetoothAdapter;
@@ -93,6 +99,8 @@
IFindDeviceCallback mFindCallback;
ICompanionDeviceDiscoveryServiceCallback mServiceCallback;
+ boolean mIsScanning = false;
+ @Nullable DeviceChooserActivity mActivity = null;
private final ICompanionDeviceDiscoveryService mBinder =
new ICompanionDeviceDiscoveryService.Stub() {
@@ -196,6 +204,10 @@
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mWifiManager.startScan();
}
+ mIsScanning = true;
+ Handler.getMain().sendMessageDelayed(
+ obtainMessage(DeviceDiscoveryService::stopScan, this),
+ SCAN_TIMEOUT);
}
private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) {
@@ -219,6 +231,15 @@
private void stopScan() {
if (DEBUG) Log.i(LOG_TAG, "stopScan()");
+ if (!mIsScanning) return;
+ mIsScanning = false;
+
+ DeviceChooserActivity activity = mActivity;
+ if (activity != null) {
+ activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator);
+ mActivity = null;
+ }
+
mBluetoothAdapter.cancelDiscovery();
if (mBluetoothBroadcastReceiver != null) {
unregisterReceiver(mBluetoothBroadcastReceiver);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 58d5db3..a75b147 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -876,47 +876,53 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration">About <xliff:g id="time">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+ <string name="power_discharging_duration_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery -->
+ <string name="power_discharge_by_enhanced">Will last until about about <xliff:g id="time">%1$s</xliff:g> based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery with no percentage -->
+ <string name="power_discharge_by_only_enhanced">Will last until about about <xliff:g id="time">%1$s</xliff:g> based on your usage</string>
+ <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
+ <string name="power_discharge_by">Will last until about about <xliff:g id="time">%1$s</xliff:g> (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
+ <string name="power_discharge_by_only">Will last until about about <xliff:g id="time">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
- <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
- <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+ <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+ <string name="power_remaining_less_than_duration">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string>
- <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
- <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
- <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
- <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+ <string name="power_remaining_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+ <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
-
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
-
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 566e037..660521e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -37,7 +37,16 @@
* Updates the current status of preference (summary, switch state, etc)
*/
public void updateState(Preference preference) {
-
+ if (preference == null) {
+ return;
+ }
+ final CharSequence summary = getSummary();
+ if (summary == null) {
+ // Default getSummary returns null. If subclass didn't override this, there is nothing
+ // we need to do.
+ return;
+ }
+ preference.setSummary(summary);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
index f68c04f..d3dc8aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -17,6 +17,8 @@
package com.android.settingslib.development;
import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -26,8 +28,9 @@
* All Preference Controllers that are a part of the developer options page should inherit this
* class.
*/
-public abstract class DeveloperOptionsPreferenceController extends
- AbstractPreferenceController {
+public abstract class DeveloperOptionsPreferenceController extends AbstractPreferenceController {
+
+ protected Preference mPreference;
public DeveloperOptionsPreferenceController(Context context) {
super(context);
@@ -43,6 +46,12 @@
return true;
}
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
/**
* Called when developer options is enabled
*/
@@ -65,12 +74,14 @@
* Called when developer options is enabled and the preference is available
*/
protected void onDeveloperOptionsSwitchEnabled() {
+ mPreference.setEnabled(true);
}
/**
* Called when developer options is disabled and the preference is available
*/
protected void onDeveloperOptionsSwitchDisabled() {
+ mPreference.setEnabled(false);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 346ca66..8b3da39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -17,22 +17,30 @@
package com.android.settingslib.utils;
import android.content.Context;
+import android.icu.text.DateFormat;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
-import com.android.settingslib.utils.StringUtil;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/** Utility class for keeping power related strings consistent**/
public class PowerUtil {
+
private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+ private static final long TWO_DAYS_MILLIS = TimeUnit.DAYS.toMillis(2);
+ private static final long ONE_HOUR_MILLIS = TimeUnit.HOURS.toMillis(1);
/**
* This method produces the text used in various places throughout the system to describe the
@@ -57,11 +65,15 @@
FIFTEEN_MINUTES_MILLIS,
false /* withSeconds */);
return getUnderFifteenString(context, timeString, percentageString);
+ } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
+ // just say more than two day if over 48 hours
+ return getMoreThanTwoDaysString(context, percentageString);
} else if (drainTimeMs >= ONE_DAY_MILLIS) {
- // just say more than one day if over 24 hours
- return getMoreThanOneDayString(context, percentageString);
+ // show remaining days & hours if more than a day
+ return getMoreThanOneDayString(context, drainTimeMs,
+ percentageString, basedOnUsage);
} else {
- // show a regular time remaining string
+ // show the time of day we think you'll run out
return getRegularTimeRemainingString(context, drainTimeMs,
percentageString, basedOnUsage);
}
@@ -83,34 +95,18 @@
? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
: context.getString(
R.string.power_remaining_less_than_duration,
- percentageString,
- timeString);
+ timeString,
+ percentageString);
}
- private static String getMoreThanOneDayString(Context context, String percentageString) {
- final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
- final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
-
- final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
-
- return TextUtils.isEmpty(percentageString)
- ? context.getString(R.string.power_remaining_only_more_than_subtext,
- frmt.formatMeasures(daysMeasure))
- : context.getString(
- R.string.power_remaining_more_than_subtext,
- percentageString,
- frmt.formatMeasures(daysMeasure));
- }
-
- private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ private static String getMoreThanOneDayString(Context context, long drainTimeMs,
String percentageString, boolean basedOnUsage) {
- // round to the nearest 15 min to not appear oversly precise
- final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
- FIFTEEN_MINUTES_MILLIS);
+ final long roundedTimeMs = roundToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
CharSequence timeString = StringUtil.formatElapsedTime(context,
roundedTimeMs,
false /* withSeconds */);
+
if (TextUtils.isEmpty(percentageString)) {
int id = basedOnUsage
? R.string.power_remaining_duration_only_enhanced
@@ -120,7 +116,48 @@
int id = basedOnUsage
? R.string.power_discharging_duration_enhanced
: R.string.power_discharging_duration;
- return context.getString(id, percentageString, timeString);
+ return context.getString(id, timeString, percentageString);
+ }
+ }
+
+ private static String getMoreThanTwoDaysString(Context context, String percentageString) {
+ final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+ final Measure daysMeasure = new Measure(2, MeasureUnit.DAY);
+
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_only_more_than_subtext,
+ frmt.formatMeasures(daysMeasure))
+ : context.getString(
+ R.string.power_remaining_more_than_subtext,
+ frmt.formatMeasures(daysMeasure),
+ percentageString);
+ }
+
+ private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ String percentageString, boolean basedOnUsage) {
+ // Get the time of day we think device will die rounded to the nearest 15 min.
+ final long roundedTimeOfDayMs =
+ roundToNearestThreshold(
+ System.currentTimeMillis() + drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+
+ // convert the time to a properly formatted string.
+ DateFormat fmt = DateFormat.getTimeInstance(DateFormat.SHORT);
+ Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
+ CharSequence timeString = fmt.format(date);
+
+ if (TextUtils.isEmpty(percentageString)) {
+ int id = basedOnUsage
+ ? R.string.power_discharge_by_only_enhanced
+ : R.string.power_discharge_by_only;
+ return context.getString(id, timeString);
+ } else {
+ int id = basedOnUsage
+ ? R.string.power_discharge_by_enhanced
+ : R.string.power_discharge_by;
+ return context.getString(id, timeString, percentageString);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 45fdd78..68be2b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -33,74 +33,74 @@
/** Utility class for generally useful string methods **/
public class StringUtil {
- public static final int SECONDS_PER_MINUTE = 60;
- public static final int SECONDS_PER_HOUR = 60 * 60;
- public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int SECONDS_PER_HOUR = 60 * 60;
+ public static final int SECONDS_PER_DAY = 24 * 60 * 60;
- /**
- * Returns elapsed time for the given millis, in the following format:
- * 2d 5h 40m 29s
- * @param context the application context
- * @param millis the elapsed time in milli seconds
- * @param withSeconds include seconds?
- * @return the formatted elapsed time
- */
- public static CharSequence formatElapsedTime(Context context, double millis,
- boolean withSeconds) {
- SpannableStringBuilder sb = new SpannableStringBuilder();
- int seconds = (int) Math.floor(millis / 1000);
- if (!withSeconds) {
- // Round up.
- seconds += 30;
- }
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 2d 5h 40m 29s
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatElapsedTime(Context context, double millis,
+ boolean withSeconds) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ int seconds = (int) Math.floor(millis / 1000);
+ if (!withSeconds) {
+ // Round up.
+ seconds += 30;
+ }
- int days = 0, hours = 0, minutes = 0;
- if (seconds >= SECONDS_PER_DAY) {
- days = seconds / SECONDS_PER_DAY;
- seconds -= days * SECONDS_PER_DAY;
- }
- if (seconds >= SECONDS_PER_HOUR) {
- hours = seconds / SECONDS_PER_HOUR;
- seconds -= hours * SECONDS_PER_HOUR;
- }
- if (seconds >= SECONDS_PER_MINUTE) {
- minutes = seconds / SECONDS_PER_MINUTE;
- seconds -= minutes * SECONDS_PER_MINUTE;
- }
+ int days = 0, hours = 0, minutes = 0;
+ if (seconds >= SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds >= SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds >= SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
- final ArrayList<Measure> measureList = new ArrayList(4);
- if (days > 0) {
- measureList.add(new Measure(days, MeasureUnit.DAY));
- }
- if (hours > 0) {
- measureList.add(new Measure(hours, MeasureUnit.HOUR));
- }
- if (minutes > 0) {
- measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
- }
- if (withSeconds && seconds > 0) {
- measureList.add(new Measure(seconds, MeasureUnit.SECOND));
- }
- if (measureList.size() == 0) {
- // Everything addable was zero, so nothing was added. We add a zero.
- measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
- }
- final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+ final ArrayList<Measure> measureList = new ArrayList(4);
+ if (days > 0) {
+ measureList.add(new Measure(days, MeasureUnit.DAY));
+ }
+ if (hours > 0) {
+ measureList.add(new Measure(hours, MeasureUnit.HOUR));
+ }
+ if (minutes > 0) {
+ measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+ if (withSeconds && seconds > 0) {
+ measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+ }
+ if (measureList.size() == 0) {
+ // Everything addable was zero, so nothing was added. We add a zero.
+ measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+ }
+ final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
- final Locale locale = context.getResources().getConfiguration().locale;
- final MeasureFormat measureFormat = MeasureFormat.getInstance(
- locale, FormatWidth.NARROW);
- sb.append(measureFormat.formatMeasures(measureArray));
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final MeasureFormat measureFormat = MeasureFormat.getInstance(
+ locale, FormatWidth.NARROW);
+ sb.append(measureFormat.formatMeasures(measureArray));
- if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
- // Add ttsSpan if it only have minute value, because it will be read as "meters"
- final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
- .setUnit("minute").build();
- sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+ // Add ttsSpan if it only have minute value, because it will be read as "meters"
+ final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+ .setUnit("minute").build();
+ sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
- return sb;
- }
+ return sb;
+ }
/**
* Returns relative time for the given millis in the past, in a short format such as "2 days
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 8767923..393fd02 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -82,8 +82,17 @@
assertThat(mPreference.isVisible()).isFalse();
}
- private class TestPrefController extends AbstractPreferenceController {
+ @Test
+ public void updateState_hasSummary_shouldSetSummary() {
+ mTestPrefController.updateState(mPreference);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TestPrefController.TEST_SUMMARY);
+ }
+
+ private static class TestPrefController extends AbstractPreferenceController {
private static final String KEY_PREF = "test_pref";
+ private static final CharSequence TEST_SUMMARY = "Test";
+
public boolean isAvailable;
public TestPrefController(Context context) {
@@ -104,6 +113,11 @@
public String getPreferenceKey() {
return KEY_PREF;
}
+
+ @Override
+ public CharSequence getSummary() {
+ return TEST_SUMMARY;
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
new file mode 100644
index 0000000..7820fd2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DeveloperOptionsPreferenceControllerTest {
+
+ private static final String TEST_KEY = "Test_pref_key";
+
+ @Mock
+ private Preference mPreference;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+
+ private DeveloperOptionsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DeveloperOptionsPreferenceControllerTestable();
+ doReturn(mPreference).when(mPreferenceScreen).findPreference(TEST_KEY);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void onDeveloperOptionsEnabled_shouldEnablePreference() {
+ mController.onDeveloperOptionsEnabled();
+
+ verify(mPreference).setEnabled(true);
+ }
+
+ @Test
+ public void onDeveloperOptionsDisabled_shouldDisablePreference() {
+ mController.onDeveloperOptionsDisabled();
+
+ verify(mPreference).setEnabled(false);
+ }
+
+ private class DeveloperOptionsPreferenceControllerTestable extends
+ DeveloperOptionsPreferenceController {
+ DeveloperOptionsPreferenceControllerTestable() {
+ super(RuntimeEnvironment.application);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return TEST_KEY;
+ }
+ }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 9285148f..c42ff08 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -24,13 +24,18 @@
import com.android.settingslib.R;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import java.time.Clock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSettings.ShadowSystem;
+import org.robolectric.shadows.ShadowSystemClock;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class PowerUtilTest {
@@ -39,8 +44,12 @@
public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
- public static final String ONE_DAY_FORMATTED = "1 day";
+ public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+ public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+ public static final String TWO_DAYS_FORMATTED = "2 days";
+ public static final String THIRTY_HOURS_FORMATTED = "1d 6h";
+ public static final String NORMAL_CASE_EXPECTED_PREFIX = "Will last until about";
+ public static final String ENHANCED_SUFFIX = "based on your usage";
private Context mContext;
@@ -51,6 +60,7 @@
}
@Test
+ @Config(shadows = {ShadowSystemClock.class})
public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
SEVENTEEN_MIN_MILLIS,
@@ -62,15 +72,13 @@
false /* basedOnUsage */);
// We only add special mention for the long string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_discharging_duration_enhanced,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info).contains(ENHANCED_SUFFIX);
+ assertThat(info).contains("%");
// shortened string should not have extra text
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_discharging_duration,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info2).doesNotContain(ENHANCED_SUFFIX);
+ assertThat(info2).contains("%");
}
@Test
@@ -84,14 +92,14 @@
null /* percentageString */,
false /* basedOnUsage */);
- // We only add special mention for the long string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only_enhanced,
- FIFTEEN_MIN_FORMATTED));
+ // We only have % when it is provided
+ assertThat(info).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info).contains(ENHANCED_SUFFIX);
+ assertThat(info).doesNotContain("%");
// shortened string should not have extra text
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info2).doesNotContain(ENHANCED_SUFFIX);
+ assertThat(info2).doesNotContain("%");
}
@@ -107,12 +115,9 @@
true /* basedOnUsage */);
// additional battery percentage in this string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_shutdown_imminent,
- TEST_BATTERY_LEVEL_10));
+ assertThat(info).isEqualTo("Phone may shutdown soon (10%)");
// shortened string should not have percentage
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only_shutdown_imminent));
+ assertThat(info2).isEqualTo("Phone may shutdown soon");
}
@Test
@@ -127,35 +132,42 @@
true /* basedOnUsage */);
// shortened string should not have percentage
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_less_than_duration_only,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info).isEqualTo("Less than 15m remaining");
// Add percentage to string when provided
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_less_than_duration,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).isEqualTo("Less than 15m remaining (10%)");
}
@Test
- public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+ public void testGetBatteryRemainingStringFormatted_betweenOneAndTwoDays_usesCorrectString() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TWO_DAYS_MILLIS,
+ THIRTY_HOURS_MILLIS,
null /* percentageString */,
true /* basedOnUsage */);
String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TWO_DAYS_MILLIS,
+ THIRTY_HOURS_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo("About 1d 6h left based on your usage");
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo("About 1d 6h left (10%)");
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanTwoDays_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ THREE_DAYS_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ THREE_DAYS_MILLIS,
TEST_BATTERY_LEVEL_10 /* percentageString */,
true /* basedOnUsage */);
// shortened string should not have percentage
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_only_more_than_subtext,
- ONE_DAY_FORMATTED));
+ assertThat(info).isEqualTo("More than 2 days remaining");
// Add percentage to string when provided
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_more_than_subtext,
- TEST_BATTERY_LEVEL_10,
- ONE_DAY_FORMATTED));
+ assertThat(info2).isEqualTo("More than 2 days remaining (10%)");
}
}
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index cd1be15..70f1cd8 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -25,41 +25,48 @@
android:baselineAligned="false"
>
- <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
- android:layout_width="@dimen/multi_user_switch_width_keyguard"
+ <LinearLayout
+ android:id="@+id/status_icon_area"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
- android:background="@drawable/ripple_drawable"
- android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin">
- <ImageView android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_keyguard_size"
- android:layout_height="@dimen/multi_user_avatar_keyguard_size"
- android:layout_gravity="center"
- android:scaleType="centerInside"/>
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
-
- <LinearLayout android:id="@+id/system_icons_super_container"
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/quick_qs_total_height"
- android:layout_toStartOf="@id/multi_user_switch"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
- android:paddingEnd="@dimen/system_icons_keyguard_padding_end">
+ android:gravity="center_vertical|end" >
<FrameLayout android:id="@+id/system_icons_container"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="@dimen/status_bar_height"
- android:layout_gravity="center_vertical"
- >
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
+ android:gravity="center_vertical|end"
+ android:paddingEnd="@dimen/system_icons_keyguard_padding_end" >
<include layout="@layout/system_icons" />
</FrameLayout>
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+ android:layout_width="@dimen/multi_user_switch_width_keyguard"
+ android:layout_height="match_parent"
+ android:background="@drawable/ripple_drawable"
+ android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin">
+ <ImageView android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
</LinearLayout>
+ <Space
+ android:id="@+id/cutout_space_view"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:visibility="gone" />
+
<com.android.keyguard.CarrierText
android:id="@+id/keyguard_carrier_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/keyguard_carrier_text_margin"
- android:layout_toStartOf="@id/system_icons_super_container"
+ android:layout_toStartOf="@id/system_icons_container"
android:gravity="center_vertical"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 2470947..5a0e767 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -16,15 +16,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/navigation_side_padding"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="0"
>
+ <!-- Use width & height=match_parent for parent FrameLayout and buttons because they are placed
+ inside a view that has a size controlled by weight. Ensure weight is large enough to support
+ icon size. -->
+
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
- android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginEnd="2dp"
android:scaleType="centerInside"
systemui:keyCode="82"
systemui:playSound="false"
@@ -33,27 +35,24 @@
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
- android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginEnd="2dp"
android:visibility="invisible"
android:contentDescription="@string/accessibility_ime_switch_button"
android:scaleType="centerInside"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/rotate_suggestion"
- android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginEnd="2dp"
android:visibility="invisible"
android:scaleType="centerInside"
android:contentDescription="@string/accessibility_rotate_button"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/accessibility_button"
- android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginEnd="2dp"
android:visibility="invisible"
android:contentDescription="@string/accessibility_accessibility_button"
android:scaleType="centerInside"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3c24845..9bf7870 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -127,7 +127,8 @@
android:id="@+id/qs_drag_handle_view"
android:layout_width="24dp"
android:layout_height="4dp"
- android:layout_gravity="center"
+ android:layout_marginBottom="16dp"
+ android:layout_gravity="center_horizontal|bottom"
android:background="@drawable/qs_footer_drag_handle" />
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 4614999..2e7ab7f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
+<!-- extends FrameLayout -->
<com.android.systemui.statusbar.ExpandableNotificationRow
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -54,6 +55,7 @@
android:paddingStart="8dp"
/>
+ <!-- TODO: remove -->
<ImageButton
android:id="@+id/helper"
android:layout_width="48dp"
@@ -64,7 +66,7 @@
android:tint="#FF0000"
android:background="@drawable/ripple_drawable"
android:visibility="visible"
- />
+ />
<ViewStub
android:layout="@layout/notification_children_container"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ca4ea9e..7b1a9e1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -353,8 +353,6 @@
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">24dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
- <!-- Difference between drag handle margin in QQS and expanded QS -->
- <dimen name="qs_footer_drag_handle_offset">10dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9245ac1..e506d2f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -694,7 +694,8 @@
<string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
<!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_input">Input</string>
-
+ <!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_label">Brightness</string>
<!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] -->
@@ -743,6 +744,8 @@
<string name="quick_settings_wifi_on_label">Wi-Fi On</string>
<!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string>
+ <!-- QuickSettings: Wifi secondary label shown when the wifi is being enabled [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_wifi_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cast_title">Cast</string>
<!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
@@ -776,7 +779,7 @@
<!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
<string name="quick_settings_hotspot_label">Hotspot</string>
<!-- QuickSettings: Hotspot. Secondary label shown when the hotspot is being enabled [CHAR LIMIT=NONE] -->
- <string name="quick_settings_hotspot_secondary_label_transient">Turning on...</string>
+ <string name="quick_settings_hotspot_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
<plurals name="quick_settings_hotspot_secondary_label_num_devices">
<item quantity="one">%d device</item>
@@ -804,10 +807,7 @@
the top of their phone's screen. This is a label for a toggle to turn the work profile on and
off. "Work profile" means a separate profile on a user's phone that's specifically for their
work apps and managed by their company. "Work" is used as an adjective. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_work_mode_on_label">Work profile</string>
- <!-- QuickSettings: This is a label for a toggle to turn the work profile on and off and this is
- shown when work profile is off. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_work_mode_off_label">Notifications & apps are off</string>
+ <string name="quick_settings_work_mode_label">Work profile</string>
<!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
<string name="quick_settings_night_display_label">Night Light</string>
<!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a01f71a..d006af1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -270,7 +270,7 @@
<style name="TextAppearance.QS.TileLabel">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:fontFamily">sans-serif</item>
</style>
<style name="BaseBrightnessDialogContainer">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 0103cad..1aad27f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -403,11 +404,11 @@
}
/**
- * @return whether there is currently a locked task (ie. in screen pinning).
+ * @return whether screen pinning is active.
*/
- public boolean isLockToAppActive() {
+ public boolean isScreenPinningActive() {
try {
- return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ return ActivityManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
} catch (RemoteException e) {
return false;
}
@@ -416,8 +417,19 @@
/**
* @return whether screen pinning is enabled.
*/
- public boolean isLockToAppEnabled() {
+ public boolean isScreenPinningEnabled() {
final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
}
+
+ /**
+ * @return whether there is currently a locked task (ie. in screen pinning).
+ */
+ public boolean isLockToAppActive() {
+ try {
+ return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 4c2aa63..2adb286 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -162,8 +162,8 @@
mRow.addView(button);
PendingIntent pendingIntent = null;
- if (rc.getContentIntent() != null) {
- pendingIntent = rc.getContentIntent().getAction();
+ if (rc.getPrimaryAction() != null) {
+ pendingIntent = rc.getPrimaryAction().getAction();
}
mClickActions.put(button, pendingIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7403ddc..cad155c 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -44,6 +44,7 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -314,6 +315,8 @@
mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+ mProviders.put(AppOpsListener.class, () -> new AppOpsListener(mContext));
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index a2c9ab4..5a2263c 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -14,7 +14,9 @@
package com.android.systemui;
+import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
public interface ForegroundServiceController {
/**
@@ -46,4 +48,32 @@
* @return true if sbn is the system-provided "dungeon" (list of running foreground services).
*/
boolean isDungeonNotification(StatusBarNotification sbn);
+
+ /**
+ * @return true if sbn is one of the window manager "drawing over other apps" notifications
+ */
+ boolean isSystemAlertNotification(StatusBarNotification sbn);
+
+ /**
+ * Returns the key of the foreground service from this package using the standard template,
+ * if one exists.
+ */
+ @Nullable String getStandardLayoutKey(int userId, String pkg);
+
+ /**
+ * @return true if this user/pkg has a missing or custom layout notification and therefore needs
+ * a disclosure notification for system alert windows.
+ */
+ boolean isSystemAlertWarningNeeded(int userId, String pkg);
+
+ /**
+ * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
+ * case they change before we have a notification to tag.
+ */
+ void onAppOpChanged(int code, int uid, String packageName, boolean active);
+
+ /**
+ * Gets active app ops for this user and package.
+ */
+ @Nullable ArraySet<Integer> getAppOps(int userId, String packageName);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
index 3714c4e..fc2b5b4 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
@@ -18,13 +18,13 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto;
import java.util.Arrays;
@@ -34,17 +34,19 @@
*/
public class ForegroundServiceControllerImpl
implements ForegroundServiceController {
-
+
// shelf life of foreground services before they go bad
public static final long FG_SERVICE_GRACE_MILLIS = 5000;
private static final String TAG = "FgServiceController";
private static final boolean DBG = false;
+ private final Context mContext;
private final SparseArray<UserServices> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
public ForegroundServiceControllerImpl(Context context) {
+ mContext = context;
}
@Override
@@ -57,6 +59,52 @@
}
@Override
+ public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) return false;
+ return services.getStandardLayoutKey(pkg) == null;
+ }
+ }
+
+ @Override
+ public String getStandardLayoutKey(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) return null;
+ return services.getStandardLayoutKey(pkg);
+ }
+ }
+
+ @Override
+ public ArraySet<Integer> getAppOps(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) {
+ return null;
+ }
+ return services.getFeatures(pkg);
+ }
+ }
+
+ @Override
+ public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
+ int userId = UserHandle.getUserId(uid);
+ synchronized (mMutex) {
+ UserServices userServices = mUserServices.get(userId);
+ if (userServices == null) {
+ userServices = new UserServices();
+ mUserServices.put(userId, userServices);
+ }
+ if (active) {
+ userServices.addOp(packageName, code);
+ } else {
+ userServices.removeOp(packageName, code);
+ }
+ }
+ }
+
+ @Override
public void addNotification(StatusBarNotification sbn, int importance) {
updateNotification(sbn, importance);
}
@@ -102,9 +150,16 @@
}
} else {
userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
- if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
- && newImportance > NotificationManager.IMPORTANCE_MIN) {
- userServices.addNotification(sbn.getPackageName(), sbn.getKey());
+ if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)) {
+ if (newImportance > NotificationManager.IMPORTANCE_MIN) {
+ userServices.addImportantNotification(sbn.getPackageName(), sbn.getKey());
+ }
+ final Notification.Builder builder = Notification.Builder.recoverBuilder(
+ mContext, sbn.getNotification());
+ if (builder.usesStandardHeader()) {
+ userServices.addStandardLayoutNotification(
+ sbn.getPackageName(), sbn.getKey());
+ }
}
}
}
@@ -117,42 +172,105 @@
&& sbn.getPackageName().equals("android");
}
+ @Override
+ public boolean isSystemAlertNotification(StatusBarNotification sbn) {
+ // TODO: tag system alert notifications so they can be suppressed if app's notification
+ // is tagged
+ return false;
+ }
+
/**
* Struct to track relevant packages and notifications for a userid's foreground services.
*/
private static class UserServices {
private String[] mRunning = null;
private long mServiceStartTime = 0;
- private ArrayMap<String, ArraySet<String>> mNotifications = new ArrayMap<>(1);
+ // package -> sufficiently important posted notification keys
+ private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
+ // package -> standard layout posted notification keys
+ private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
+
+ // package -> app ops
+ private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
+
public void setRunningServices(String[] pkgs, long serviceStartTime) {
mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
mServiceStartTime = serviceStartTime;
}
- public void addNotification(String pkg, String key) {
- if (mNotifications.get(pkg) == null) {
- mNotifications.put(pkg, new ArraySet<String>());
+
+ public void addOp(String pkg, int op) {
+ if (mAppOps.get(pkg) == null) {
+ mAppOps.put(pkg, new ArraySet<>(3));
}
- mNotifications.get(pkg).add(key);
+ mAppOps.get(pkg).add(op);
}
- public boolean removeNotification(String pkg, String key) {
+
+ public boolean removeOp(String pkg, int op) {
final boolean found;
- final ArraySet<String> keys = mNotifications.get(pkg);
+ final ArraySet<Integer> keys = mAppOps.get(pkg);
+ if (keys == null) {
+ found = false;
+ } else {
+ found = keys.remove(op);
+ if (keys.size() == 0) {
+ mAppOps.remove(pkg);
+ }
+ }
+ return found;
+ }
+
+ public void addImportantNotification(String pkg, String key) {
+ addNotification(mImportantNotifications, pkg, key);
+ }
+
+ public boolean removeImportantNotification(String pkg, String key) {
+ return removeNotification(mImportantNotifications, pkg, key);
+ }
+
+ public void addStandardLayoutNotification(String pkg, String key) {
+ addNotification(mStandardLayoutNotifications, pkg, key);
+ }
+
+ public boolean removeStandardLayoutNotification(String pkg, String key) {
+ return removeNotification(mStandardLayoutNotifications, pkg, key);
+ }
+
+ public boolean removeNotification(String pkg, String key) {
+ boolean removed = false;
+ removed |= removeImportantNotification(pkg, key);
+ removed |= removeStandardLayoutNotification(pkg, key);
+ return removed;
+ }
+
+ public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
+ String key) {
+ if (map.get(pkg) == null) {
+ map.put(pkg, new ArraySet<>());
+ }
+ map.get(pkg).add(key);
+ }
+
+ public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
+ String pkg, String key) {
+ final boolean found;
+ final ArraySet<String> keys = map.get(pkg);
if (keys == null) {
found = false;
} else {
found = keys.remove(key);
if (keys.size() == 0) {
- mNotifications.remove(pkg);
+ map.remove(pkg);
}
}
return found;
}
+
public boolean isDungeonNeeded() {
if (mRunning != null
&& System.currentTimeMillis() - mServiceStartTime >= FG_SERVICE_GRACE_MILLIS) {
for (String pkg : mRunning) {
- final ArraySet<String> set = mNotifications.get(pkg);
+ final ArraySet<String> set = mImportantNotifications.get(pkg);
if (set == null || set.size() == 0) {
return true;
}
@@ -160,5 +278,27 @@
}
return false;
}
+
+ public ArraySet<Integer> getFeatures(String pkg) {
+ return mAppOps.get(pkg);
+ }
+
+ public String getStandardLayoutKey(String pkg) {
+ final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
+ if (set == null || set.size() == 0) {
+ return null;
+ }
+ return set.valueAt(0);
+ }
+
+ @Override
+ public String toString() {
+ return "UserServices{" +
+ "mRunning=" + Arrays.toString(mRunning) +
+ ", mServiceStartTime=" + mServiceStartTime +
+ ", mImportantNotifications=" + mImportantNotifications +
+ ", mStandardLayoutNotifications=" + mStandardLayoutNotifications +
+ '}';
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 7b48e02..e9888df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -86,14 +86,9 @@
private View mActionsContainer;
private View mDragHandle;
- private final int mDragHandleExpandOffset;
public QSFooterImpl(Context context, AttributeSet attrs) {
super(context, attrs);
-
- mDragHandleExpandOffset = getResources().
- getDimensionPixelSize(R.dimen.qs_footer_drag_handle_offset);
-
}
@Override
@@ -171,7 +166,6 @@
.addFloat(mDivider, "alpha", 0, 1)
.addFloat(mCarrierText, "alpha", 0, 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
- .addFloat(mDragHandle, "translationY", mDragHandleExpandOffset, 0)
.addFloat(mDragHandle, "alpha", 1, 0, 0)
.setStartDelay(0.15f)
.build();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 04dbb88..24ddafc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -66,6 +66,7 @@
protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
+ protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
protected final QSHost mHost;
protected final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 2a9a381..3b79db7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -90,6 +90,8 @@
protected void handleClick() {
// Secondary clicks are header clicks, just toggle.
final boolean isEnabled = mState.value;
+ // Immediately enter transient enabling state when turning bluetooth on.
+ refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
mController.setBluetoothEnabled(!isEnabled);
}
@@ -118,9 +120,10 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- final boolean enabled = mController.isBluetoothEnabled();
+ final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
+ final boolean enabled = transientEnabling || mController.isBluetoothEnabled();
final boolean connected = mController.isBluetoothConnected();
- state.isTransient = mController.isBluetoothConnecting()
+ state.isTransient = transientEnabling || mController.isBluetoothConnecting()
|| mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
state.dualTarget = true;
state.value = enabled;
@@ -129,7 +132,6 @@
}
state.slash.isSlashed = !enabled;
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
-
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
@@ -155,8 +157,7 @@
state.state = Tile.STATE_INACTIVE;
}
- state.secondaryLabel = getSecondaryLabel(enabled, connected);
-
+ state.secondaryLabel = getSecondaryLabel(enabled, connected, state.isTransient);
state.dualLabelContentDescription = mContext.getResources().getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
@@ -169,9 +170,13 @@
*
* @param enabled whether bluetooth is enabled
* @param connected whether there's a device connected via bluetooth
+ * @param isTransient whether bluetooth is currently in a transient state turning on
*/
@Nullable
- private String getSecondaryLabel(boolean enabled, boolean connected) {
+ private String getSecondaryLabel(boolean enabled, boolean connected, boolean isTransient) {
+ if (isTransient) {
+ return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
+ }
final CachedBluetoothDevice lastDevice = mController.getLastDevice();
if (enabled && connected && lastDevice != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 080e320..d675f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -21,7 +21,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserManager;
-
import android.provider.Settings.Global;
import android.service.quicksettings.Tile;
import android.widget.Switch;
@@ -29,9 +28,9 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QSTile.AirplaneBooleanState;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.plugins.qs.QSTile.AirplaneBooleanState;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -96,10 +95,12 @@
@Override
protected void handleClick() {
- final boolean isEnabled = (Boolean) mState.value;
+ final boolean isEnabled = mState.value;
if (!isEnabled && mAirplaneMode.getValue() != 0) {
return;
}
+ // Immediately enter transient enabling state when turning hotspot on.
+ refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
mController.setHotspotEnabled(!isEnabled);
}
@@ -110,12 +111,13 @@
@Override
protected void handleUpdateState(AirplaneBooleanState state, Object arg) {
+ final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
if (state.slash == null) {
state.slash = new SlashState();
}
final int numConnectedDevices;
- final boolean isTransient = mController.isHotspotTransient();
+ final boolean isTransient = transientEnabling || mController.isHotspotTransient();
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING);
if (arg instanceof CallbackInfo) {
@@ -123,7 +125,7 @@
state.value = info.enabled;
numConnectedDevices = info.numConnectedDevices;
} else {
- state.value = mController.isHotspotEnabled();
+ state.value = transientEnabling || mController.isHotspotEnabled();
numConnectedDevices = mController.getNumConnectedDevices();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 540d28e..3ad3940 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -117,7 +117,10 @@
protected void handleClick() {
// Secondary clicks are header clicks, just toggle.
mState.copyTo(mStateBeforeClick);
- mController.setWifiEnabled(!mState.value);
+ boolean wifiEnabled = mState.value;
+ // Immediately enter transient state when turning on wifi.
+ refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
+ mController.setWifiEnabled(!wifiEnabled);
}
@Override
@@ -141,11 +144,13 @@
@Override
protected void handleUpdateState(SignalState state, Object arg) {
if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
- CallbackInfo cb = (CallbackInfo) arg;
- if (cb == null) {
+ final CallbackInfo cb;
+ if (arg != null && arg instanceof CallbackInfo) {
+ cb = (CallbackInfo) arg;
+ } else {
cb = mSignalCallback.mInfo;
}
-
+ boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
boolean enabledChanging = state.value != cb.enabled;
@@ -158,14 +163,16 @@
state.slash.rotation = 6;
}
state.slash.isSlashed = false;
+ boolean isTransient = transientEnabling || cb.isTransient;
+ state.secondaryLabel = getSecondaryLabel(isTransient);
state.state = Tile.STATE_ACTIVE;
state.dualTarget = true;
- state.value = cb.enabled;
+ state.value = transientEnabling || cb.enabled;
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
final StringBuffer minimalContentDescription = new StringBuffer();
final Resources r = mContext.getResources();
- if (cb.isTransient) {
+ if (isTransient) {
state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation);
state.label = r.getString(R.string.quick_settings_wifi_label);
} else if (!state.value) {
@@ -197,6 +204,12 @@
state.expandedAccessibilityClassName = Switch.class.getName();
}
+ private CharSequence getSecondaryLabel(boolean isTransient) {
+ return isTransient
+ ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
+ : null;
+ }
+
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_WIFI;
@@ -277,8 +290,6 @@
}
}
- ;
-
protected class WifiDetailAdapter implements DetailAdapter,
NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index e098fd8..8593249 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -103,13 +103,12 @@
state.slash.isSlashed = false;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_work_mode_on);
- state.label = mContext.getString(R.string.quick_settings_work_mode_on_label);
} else {
state.slash.isSlashed = true;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_work_mode_off);
- state.label = mContext.getString(R.string.quick_settings_work_mode_off_label);
}
+ state.label = mContext.getString(R.string.quick_settings_work_mode_label);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 47b0de9..df4a975 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -449,7 +449,7 @@
final int activityType = runningTask != null
? runningTask.configuration.windowConfiguration.getActivityType()
: ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive();
+ boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
boolean isRunningTaskInHomeOrRecentsStack =
activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 3f6f30b..055e72e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,7 +24,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.KeyguardManager;
import android.app.trust.TrustManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -34,7 +33,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask.Status;
import android.os.Handler;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -385,8 +383,7 @@
}
public void toggleRecents(int growTarget) {
- // Skip preloading if the task is locked
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return;
}
@@ -464,8 +461,7 @@
}
public void preloadRecents() {
- // Skip preloading if the task is locked
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 3cc3273..89288d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -2188,7 +2188,8 @@
private void readSystemFlags() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled();
+ mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isScreenPinningEnabled()
+ && !ActivityManagerWrapper.getInstance().isLockToAppActive();
}
private void updateStackActionButtonVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
new file mode 100644
index 0000000..2ec78cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+
+/**
+ * This class handles listening to notification updates and passing them along to
+ * NotificationPresenter to be displayed to the user.
+ */
+public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener {
+ private static final String TAG = "NotificationListener";
+
+ // Dependencies:
+ private final ForegroundServiceController mFsc =
+ Dependency.get(ForegroundServiceController.class);
+
+ private final Context mContext;
+ protected NotificationPresenter mPresenter;
+ protected NotificationEntryManager mEntryManager;
+ protected final AppOpsManager mAppOps;
+
+ protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ AppOpsManager.OP_RECORD_AUDIO};
+
+ public AppOpsListener(Context context) {
+ mContext = context;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ }
+
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationEntryManager entryManager) {
+ mPresenter = presenter;
+ mEntryManager = entryManager;
+ mAppOps.startWatchingActive(OPS, this);
+ }
+
+ public void destroy() {
+ mAppOps.stopWatchingActive(this);
+ }
+
+ @Override
+ public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
+ mFsc.onAppOpChanged(code, uid, packageName, active);
+ mPresenter.getHandler().post(() -> {
+ mEntryManager.updateNotificationsForAppOps(code, uid, packageName, active);
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index bc2dff9..785fc1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.MathUtils;
@@ -1354,6 +1355,14 @@
mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
}
+ public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+ if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
+ mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
+ }
+ mPrivateLayout.showAppOpsIcons(activeOps);
+ mPublicLayout.showAppOpsIcons(activeOps);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -2629,6 +2638,16 @@
mChildrenContainer = childrenContainer;
}
+ @VisibleForTesting
+ protected void setPrivateLayout(NotificationContentView privateLayout) {
+ mPrivateLayout = privateLayout;
+ }
+
+ @VisibleForTesting
+ protected void setPublicLayout(NotificationContentView publicLayout) {
+ mPublicLayout = publicLayout;
+ }
+
/**
* Equivalent to View.OnLongClickListener with coordinates
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 91960df..73c8795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.view.NotificationHeaderView;
@@ -1423,6 +1424,17 @@
return header;
}
+ public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+ if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
+ mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
+ mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
+ mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ }
public NotificationHeaderView getContractedNotificationHeader() {
if (mContractedChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 127f3f9..d53cb03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -34,6 +35,7 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -65,6 +67,8 @@
private final Environment mEnvironment;
private HeadsUpManager mHeadsUpManager;
+ final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class);
+
public static final class Entry {
private static final long LAUNCH_COOLDOWN = 2000;
private static final long REMOTE_INPUT_COOLDOWN = 500;
@@ -95,6 +99,7 @@
private Throwable mDebugThrowable;
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+ public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
public Entry(StatusBarNotification n) {
this.key = n.getKey();
@@ -194,7 +199,7 @@
/**
* Update the notification icons.
* @param context the context to create the icons with.
- * @param n the notification to read the icon from.
+ * @param sbn the notification to read the icon from.
* @throws InflationException
*/
public void updateIcons(Context context, StatusBarNotification sbn)
@@ -375,6 +380,8 @@
}
mGroupManager.onEntryAdded(entry);
+ updateAppOps(entry);
+
updateRankingAndSort(mRankingMap);
}
@@ -393,6 +400,35 @@
updateRankingAndSort(ranking);
}
+ private void updateAppOps(Entry entry) {
+ final int uid = entry.notification.getUid();
+ final String pkg = entry.notification.getPackageName();
+ ArraySet<Integer> activeOps = mFsc.getAppOps(entry.notification.getUserId(), pkg);
+ if (activeOps != null) {
+ int N = activeOps.size();
+ for (int i = 0; i < N; i++) {
+ updateAppOp(activeOps.valueAt(i), uid, pkg, true);
+ }
+ }
+ }
+
+ public void updateAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+ synchronized (mEntries) {
+ final int N = mEntries.size();
+ for (int i = 0; i < N; i++) {
+ Entry entry = mEntries.valueAt(i);
+ if (uid == entry.notification.getUid()
+ && pkg.equals(entry.notification.getPackageName())) {
+ if (showIcon) {
+ entry.mActiveAppOps.add(appOp);
+ } else {
+ entry.mActiveAppOps.remove(appOp);
+ }
+ }
+ }
+ }
+ }
+
public boolean isAmbient(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
@@ -545,11 +581,14 @@
return true;
}
- final ForegroundServiceController fsc = Dependency.get(ForegroundServiceController.class);
- if (fsc.isDungeonNotification(sbn) && !fsc.isDungeonNeededForUser(sbn.getUserId())) {
+ if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) {
// this is a foreground-service disclosure for a user that does not need to show one
return true;
}
+ if (mFsc.isSystemAlertNotification(sbn) && !mFsc.isSystemAlertWarningNeeded(
+ sbn.getUserId(), sbn.getPackageName())) {
+ return true;
+ }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7360486..71f7911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
@@ -77,7 +78,7 @@
public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
VisualStabilityManager.Callback {
- private static final String TAG = "NotificationEntryManager";
+ private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = false;
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
@@ -734,6 +735,14 @@
}
}
+ public void updateNotificationsForAppOps(int appOp, int uid, String pkg, boolean showIcon) {
+ if (mForegroundServiceController.getStandardLayoutKey(
+ UserHandle.getUserId(uid), pkg) != null) {
+ mNotificationData.updateAppOp(appOp, uid, pkg, showIcon);
+ updateNotifications();
+ }
+ }
+
private boolean alertAgain(NotificationData.Entry oldEntry, Notification newNotification) {
return oldEntry == null || !oldEntry.hasInterrupted()
|| (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index cd4c7ae..75b8b37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -342,6 +342,8 @@
row.showBlockingHelper(entry.userSentiment ==
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
+ row.showAppOpsIcons(entry.mActiveAppOps);
}
mPresenter.onUpdateRowStates();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aba5cdf..d2cdc27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.v4.util.ArraySet;
import android.util.Log;
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -45,12 +47,12 @@
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener {
+ OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
private static final String TAG = "HeadsUpManagerPhone";
private static final boolean DEBUG = false;
private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
+ private int mStatusBarHeight;
private final NotificationGroupManager mGroupManager;
private final StatusBar mBar;
private final VisualStabilityManager mVisualStabilityManager;
@@ -291,6 +293,13 @@
}
}
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ Resources resources = mContext.getResources();
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// VisualStabilityManager.Callback overrides:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 2375b6a..c5269ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -26,10 +26,13 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -59,6 +62,10 @@
public class KeyguardStatusBarView extends RelativeLayout
implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener {
+ private static final int LAYOUT_NONE = 0;
+ private static final int LAYOUT_CUTOUT = 1;
+ private static final int LAYOUT_NO_CUTOUT = 2;
+
private boolean mBatteryCharging;
private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
@@ -78,6 +85,10 @@
private View mSystemIconsContainer;
private TintedIconManager mIconManager;
+ private View mCutoutSpace;
+ private ViewGroup mStatusIconArea;
+ private int mLayoutState = LAYOUT_NONE;
+
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -85,12 +96,13 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
mSystemIconsContainer = findViewById(R.id.system_icons_container);
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = findViewById(R.id.multi_user_avatar);
mCarrierLabel = findViewById(R.id.keyguard_carrier_text);
mBatteryView = mSystemIconsContainer.findViewById(R.id.battery);
+ mCutoutSpace = findViewById(R.id.cutout_space_view);
+ mStatusIconArea = findViewById(R.id.status_icon_area);
loadDimens();
updateUserSwitcher();
@@ -106,6 +118,7 @@
R.dimen.multi_user_avatar_keyguard_size);
mMultiUserAvatar.setLayoutParams(lp);
+ // Multi-user switch
lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(
R.dimen.multi_user_switch_width_keyguard);
@@ -113,21 +126,21 @@
R.dimen.multi_user_switch_keyguard_margin));
mMultiUserSwitch.setLayoutParams(lp);
- lp = (MarginLayoutParams) mSystemIconsSuperContainer.getLayoutParams();
+ // System icons
+ lp = (MarginLayoutParams) mStatusIconArea.getLayoutParams();
lp.height = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_total_height);
- lp.setMarginStart(getResources().getDimensionPixelSize(
- R.dimen.system_icons_super_container_margin_start));
- mSystemIconsSuperContainer.setLayoutParams(lp);
- mSystemIconsSuperContainer.setPaddingRelative(mSystemIconsSuperContainer.getPaddingStart(),
- mSystemIconsSuperContainer.getPaddingTop(),
- getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end),
- mSystemIconsSuperContainer.getPaddingBottom());
+ com.android.internal.R.dimen.status_bar_height);
lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams();
lp.height = getResources().getDimensionPixelSize(
R.dimen.status_bar_height);
+ lp.setMarginStart(getResources().getDimensionPixelSize(
+ R.dimen.system_icons_super_container_margin_start));
mSystemIconsContainer.setLayoutParams(lp);
+ mSystemIconsContainer.setPaddingRelative(mSystemIconsContainer.getPaddingStart(),
+ mSystemIconsContainer.getPaddingTop(),
+ getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end),
+ mSystemIconsContainer.getPaddingBottom());
// Respect font size setting.
mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -153,13 +166,13 @@
}
private void updateVisibilities() {
- if (mMultiUserSwitch.getParent() != this && !mKeyguardUserSwitcherShowing) {
+ if (mMultiUserSwitch.getParent() != mStatusIconArea && !mKeyguardUserSwitcherShowing) {
if (mMultiUserSwitch.getParent() != null) {
getOverlay().remove(mMultiUserSwitch);
}
- addView(mMultiUserSwitch, 0);
- } else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) {
- removeView(mMultiUserSwitch);
+ mStatusIconArea.addView(mMultiUserSwitch, 0);
+ } else if (mMultiUserSwitch.getParent() == mStatusIconArea && mKeyguardUserSwitcherShowing) {
+ mStatusIconArea.removeView(mMultiUserSwitch);
}
if (mKeyguardUserSwitcher == null) {
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
@@ -175,8 +188,8 @@
}
private void updateSystemIconsLayoutParams() {
- RelativeLayout.LayoutParams lp =
- (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
+ LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
// If the avatar icon is gone, we need to have some end margin to display the system icons
// correctly.
int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
@@ -186,10 +199,88 @@
baseMarginEnd;
if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
- mSystemIconsSuperContainer.setLayoutParams(lp);
+ mSystemIconsContainer.setLayoutParams(lp);
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ updateLayoutConsideringCutout();
+ setSignalClusterLayoutWidth();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ private void updateLayoutConsideringCutout() {
+ DisplayCutout dc = getRootWindowInsets().getDisplayCutout();
+ if (dc == null) {
+ updateLayoutParamsNoCutout();
+ } else {
+ updateLayoutParamsForCutout(dc);
+ }
+ }
+
+ private void updateLayoutParamsNoCutout() {
+ if (mLayoutState == LAYOUT_NO_CUTOUT) {
+ return;
+ }
+ mLayoutState = LAYOUT_NO_CUTOUT;
+
+ if (mCutoutSpace != null) {
+ mCutoutSpace.setVisibility(View.GONE);
+ }
+
+ RelativeLayout.LayoutParams lp = (LayoutParams) mCarrierLabel.getLayoutParams();
+ lp.addRule(RelativeLayout.START_OF, R.id.status_icon_area);
+
+ lp = (LayoutParams) mStatusIconArea.getLayoutParams();
+ lp.removeRule(RelativeLayout.RIGHT_OF);
+ lp.width = LayoutParams.WRAP_CONTENT;
+
+ LinearLayout.LayoutParams llp =
+ (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
+ llp.setMarginStart(getResources().getDimensionPixelSize(
+ R.dimen.system_icons_super_container_margin_start));
+ }
+
+ private void updateLayoutParamsForCutout(DisplayCutout dc) {
+ if (mLayoutState == LAYOUT_CUTOUT) {
+ return;
+ }
+ mLayoutState = LAYOUT_CUTOUT;
+
+ if (mCutoutSpace == null) {
+ updateLayoutParamsNoCutout();
+ }
+
+ mCutoutSpace.setVisibility(View.VISIBLE);
+ RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams();
+ lp.width = dc.getBoundingRect().width();
+ lp.height = dc.getBoundingRect().height();
+ lp.addRule(RelativeLayout.CENTER_IN_PARENT);
+
+ lp = (LayoutParams) mCarrierLabel.getLayoutParams();
+ lp.addRule(RelativeLayout.START_OF, R.id.cutout_space_view);
+
+ lp = (LayoutParams) mStatusIconArea.getLayoutParams();
+ lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view);
+ lp.width = LayoutParams.MATCH_PARENT;
+
+ LinearLayout.LayoutParams llp =
+ (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
+ llp.setMarginStart(0);
+ }
+
+ //TODO: Something is setting signal_cluster to MATCH_PARENT. Why?
+ private void setSignalClusterLayoutWidth() {
+ View signalCluster = findViewById(R.id.signal_cluster);
+ if (signalCluster == null) {
+ return;
+ }
+
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams();
+ lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
+ }
+
public void setListening(boolean listening) {
if (listening == mBatteryListening) {
return;
@@ -265,20 +356,21 @@
animateNextLayoutChange();
}
updateVisibilities();
+ updateLayoutConsideringCutout();
updateSystemIconsLayoutParams();
}
private void animateNextLayoutChange() {
- final int systemIconsCurrentX = mSystemIconsSuperContainer.getLeft();
- final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == this;
+ final int systemIconsCurrentX = mSystemIconsContainer.getLeft();
+ final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == mStatusIconArea;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
boolean userSwitcherHiding = userSwitcherVisible
- && mMultiUserSwitch.getParent() != KeyguardStatusBarView.this;
- mSystemIconsSuperContainer.setX(systemIconsCurrentX);
- mSystemIconsSuperContainer.animate()
+ && mMultiUserSwitch.getParent() != mStatusIconArea;
+ mSystemIconsContainer.setX(systemIconsCurrentX);
+ mSystemIconsContainer.animate()
.translationX(0)
.setDuration(400)
.setStartDelay(userSwitcherHiding ? 300 : 0)
@@ -315,8 +407,8 @@
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (visibility != View.VISIBLE) {
- mSystemIconsSuperContainer.animate().cancel();
- mSystemIconsSuperContainer.setTranslationX(0);
+ mSystemIconsContainer.animate().cancel();
+ mSystemIconsContainer.setTranslationX(0);
mMultiUserSwitch.animate().cancel();
mMultiUserSwitch.setAlpha(1f);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 1c361ca..d61d6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -260,9 +260,15 @@
pw.print(" mScrimAlpha="); pw.print(mScrimAlpha);
pw.print(" mScrimAlphaBelowThreshold="); pw.println(mScrimAlphaBelowThreshold);
pw.println();
- pw.println(" StatusBarTransitionsController:");
- mStatusBarIconController.getTransitionsController().dump(fd, pw, args);
- pw.println();
+
+ LightBarTransitionsController transitionsController =
+ mStatusBarIconController.getTransitionsController();
+ if (transitionsController != null) {
+ pw.println(" StatusBarTransitionsController:");
+ transitionsController.dump(fd, pw, args);
+ pw.println();
+ }
+
if (mNavigationBarController != null) {
pw.println(" NavigationBarTransitionsController:");
mNavigationBarController.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 4d99a46..2779820 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -52,6 +52,21 @@
EventLogTags.writeSysuiLockscreenGesture(safeLookup(gesture), length, velocity);
}
+ /**
+ * Record the location of a swipe gesture, expressed as percentages of the whole screen
+ * @param category the action
+ * @param xPercent x-location / width * 100
+ * @param yPercent y-location / height * 100
+ */
+ public void writeAtFractionalPosition(
+ int category, int xPercent, int yPercent, int rotation) {
+ mMetricsLogger.write(mLogMaker.setCategory(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_GESTURE_X_PERCENT, xPercent)
+ .addTaggedData(MetricsEvent.FIELD_GESTURE_Y_PERCENT, yPercent)
+ .addTaggedData(MetricsEvent.FIELD_DEVICE_ROTATION, rotation));
+ }
+
private int safeLookup(int gesture) {
Integer value = mLegacyMap.get(gesture);
if (value == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 62151cf..0ed69e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -708,7 +708,8 @@
@VisibleForTesting
boolean onHomeLongClick(View v) {
- if (!mNavigationBarView.isRecentsButtonVisible() && mNavigationBarView.inScreenPinning()) {
+ if (!mNavigationBarView.isRecentsButtonVisible()
+ && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return onLongPressBackHome(v);
}
if (shouldDisableNavbarGestures()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 320b56f..6a1ed51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -149,7 +150,8 @@
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
+ if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()
+ || !mStatusBar.isPresenterFullyCollapsed()) {
return false;
}
@@ -168,7 +170,7 @@
}
}
boolean handledByQuickscrub = mQuickScrubController.onInterceptTouchEvent(event);
- if (mStatusBar.isPresenterFullyCollapsed() && !handledByQuickscrub) {
+ if (!handledByQuickscrub) {
// Proxy motion events until we start intercepting for quickscrub
proxyMotionEvents(event);
}
@@ -182,17 +184,17 @@
}
public boolean onTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
+ if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()
+ || !mStatusBar.isPresenterFullyCollapsed()) {
return false;
}
// The same down event was just sent on intercept and therefore can be ignored here
boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
&& mOverviewProxyService.getProxy() != null;
- boolean result = mStatusBar.isPresenterFullyCollapsed()
- && (mQuickScrubController.onTouchEvent(event)
+ boolean result = mQuickScrubController.onTouchEvent(event)
|| ignoreProxyDownEvent
- || proxyMotionEvents(event));
+ || proxyMotionEvents(event);
result |= mRecentsAnimationStarted;
if (mDockWindowEnabled) {
result |= handleDockWindowEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index a5621e5..74fbed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -21,8 +21,6 @@
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
@@ -30,7 +28,6 @@
import android.animation.ValueAnimator;
import android.annotation.DrawableRes;
import android.annotation.StyleRes;
-import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -41,7 +38,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
@@ -60,7 +56,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
-import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
@@ -379,15 +374,20 @@
return getRecentsButton().getVisibility() == View.VISIBLE;
}
+ public boolean isOverviewEnabled() {
+ return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0;
+ }
+
public boolean isQuickStepSwipeUpEnabled() {
return mOverviewProxyService.getProxy() != null
+ && isOverviewEnabled()
&& ((mOverviewProxyService.getInteractionFlags()
& FLAG_DISABLE_SWIPE_UP) == 0);
}
public boolean isQuickScrubEnabled() {
return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
- && mOverviewProxyService.getProxy() != null && !isRecentsButtonVisible()
+ && mOverviewProxyService.getProxy() != null && isOverviewEnabled()
&& ((mOverviewProxyService.getInteractionFlags()
& FLAG_DISABLE_QUICK_SCRUB) == 0);
}
@@ -575,8 +575,7 @@
boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
// Always disable recents when alternate car mode UI is active.
- boolean disableRecent = mUseCarModeUi
- || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
+ boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
&& ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
@@ -584,17 +583,18 @@
// When screen pinning, don't hide back and home when connected service or back and
// recents buttons when disconnected from launcher service in screen pinning mode,
// as they are used for exiting.
+ final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
if (mOverviewProxyService.getProxy() != null) {
// Use interaction flags to show/hide navigation buttons but will be shown if required
// to exit screen pinning.
final int flags = mOverviewProxyService.getInteractionFlags();
disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
- if (inScreenPinning()) {
+ if (pinningActive) {
disableBack = disableHome = false;
} else {
disableBack |= (flags & FLAG_HIDE_BACK_BUTTON) != 0;
}
- } else if (inScreenPinning()) {
+ } else if (pinningActive) {
disableBack = disableRecent = false;
}
@@ -614,7 +614,7 @@
}
public boolean inScreenPinning() {
- return ActivityManagerWrapper.getInstance().isLockToAppActive();
+ return ActivityManagerWrapper.getInstance().isScreenPinningActive();
}
public void setLayoutTransitionsEnabled(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6daabed..9c2a0e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -342,7 +342,7 @@
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()) {
- startOpening();
+ startOpening(event);
}
break;
@@ -417,7 +417,7 @@
return !mGestureWaitForTouchSlop || mTracking;
}
- private void startOpening() {;
+ private void startOpening(MotionEvent event) {
runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
false /* collapseWhenFinished */);
notifyBarPanelExpansionChanged();
@@ -425,6 +425,18 @@
AsyncTask.execute(() ->
mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)));
}
+
+ //TODO: keyguard opens QS a different way; log that too?
+
+ // Log the position of the swipe that opened the panel
+ float width = mStatusBar.getDisplayWidth();
+ float height = mStatusBar.getDisplayHeight();
+ int rot = mStatusBar.getRotation();
+
+ mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
+ (int) (event.getX() / width * 100),
+ (int) (event.getY() / height * 100),
+ rot);
}
protected abstract float getOpeningHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index cc5a93c..900ec0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -264,7 +264,7 @@
mScrimController.setPanelExpansion(scrimFraction);
}
- public void onDensityOrFontScaleChanged() {
+ public void updateResources() {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = getResources().getDimensionPixelSize(
R.dimen.status_bar_height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a31727e..408cd85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -180,6 +180,7 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -405,6 +406,7 @@
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
protected NotificationViewHierarchyManager mViewHierarchyManager;
+ protected AppOpsListener mAppOpsListener;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -622,6 +624,8 @@
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
+ mAppOpsListener = Dependency.get(AppOpsListener.class);
+ mAppOpsListener.setUpWithPresenter(this, mEntryManager);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
@@ -813,6 +817,7 @@
mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
mVisualStabilityManager);
+ Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
@@ -1069,7 +1074,6 @@
// end old BaseStatusBar.onDensityOrFontScaleChanged().
mScrimController.onDensityOrFontScaleChanged();
// TODO: Remove this.
- if (mStatusBarView != null) mStatusBarView.onDensityOrFontScaleChanged();
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
}
@@ -2830,6 +2834,18 @@
return mDisplayMetrics.density;
}
+ float getDisplayWidth() {
+ return mDisplayMetrics.widthPixels;
+ }
+
+ float getDisplayHeight() {
+ return mDisplayMetrics.heightPixels;
+ }
+
+ int getRotation() {
+ return mDisplay.getRotation();
+ }
+
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
boolean dismissShade) {
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
@@ -3079,6 +3095,9 @@
loadDimens();
+ if (mStatusBarView != null) {
+ mStatusBarView.updateResources();
+ }
if (mNotificationPanel != null) {
mNotificationPanel.updateResources();
}
@@ -3293,6 +3312,7 @@
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
mDeviceProvisionedController.removeCallback(mUserSetupObserver);
Dependency.get(ConfigurationController.class).removeCallback(this);
+ mAppOpsListener.destroy();
}
private boolean mDemoModeAllowed;
@@ -4519,7 +4539,7 @@
if (isScreenTurningOnOrOn()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.hideBouncer(false /* destroyView */);
+ mStatusBarKeyguardViewManager.reset(true /* hide */);
}
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 49cffc0..a009d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -164,7 +164,7 @@
updateStates();
}
- public void hideBouncer(boolean destroyView) {
+ private void hideBouncer(boolean destroyView) {
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index dba89479..dab28d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -69,10 +69,14 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
+
final int count = getChildCount();
// Measure all children so that they report the correct width
for (int i = 0; i < count; i++) {
- measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+ measureChild(getChildAt(i), widthSpec, heightMeasureSpec);
}
}
@@ -99,7 +103,8 @@
int childCount = getChildCount();
// Underflow === don't show content until that index
int firstUnderflowIndex = -1;
- if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+ if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX
+ + " width=" + width);
//TODO: Dots
for (int i = childCount - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 8792b4f..d6d0673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -140,7 +140,6 @@
if (DEBUG) Log.d(TAG, "Starting tethering");
mConnectivityManager.startTethering(
ConnectivityManager.TETHERING_WIFI, false, callback);
- fireHotspotChangedCallback(isHotspotEnabled());
} else {
mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index a2bec98..cc7943b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -26,11 +26,9 @@
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import com.android.systemui.Interpolators;
@@ -58,17 +56,14 @@
private float mGlowAlpha = 0f;
private float mGlowScale = 1f;
private boolean mPressed;
- private boolean mVisible;
private boolean mDrawingHardwareGlow;
private int mMaxWidth;
private boolean mLastDark;
private boolean mDark;
- private boolean mDelayTouchFeedback;
private final Interpolator mInterpolator = new LogInterpolator();
private boolean mSupportHardware;
private final View mTargetView;
- private final Handler mHandler = new Handler();
private final HashSet<Animator> mRunningAnimations = new HashSet<>();
private final ArrayList<Animator> mTmpArray = new ArrayList<>();
@@ -82,10 +77,6 @@
mDark = darkIntensity >= 0.5f;
}
- public void setDelayTouchFeedback(boolean delay) {
- mDelayTouchFeedback = delay;
- }
-
private Paint getRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
@@ -220,16 +211,7 @@
}
}
- /**
- * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
- * is enabled.
- */
- public void abortDelayedRipple() {
- mHandler.removeCallbacksAndMessages(null);
- }
-
private void cancelAnimations() {
- mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
for (int i = 0; i < size; i++) {
@@ -238,21 +220,11 @@
}
mTmpArray.clear();
mRunningAnimations.clear();
- mHandler.removeCallbacksAndMessages(null);
}
private void setPressedSoftware(boolean pressed) {
if (pressed) {
- if (mDelayTouchFeedback) {
- if (mRunningAnimations.isEmpty()) {
- mHandler.removeCallbacksAndMessages(null);
- mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
- } else if (mVisible) {
- enterSoftware();
- }
- } else {
- enterSoftware();
- }
+ enterSoftware();
} else {
exitSoftware();
}
@@ -260,7 +232,6 @@
private void enterSoftware() {
cancelAnimations();
- mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
0f, GLOW_MAX_SCALE_FACTOR);
@@ -269,12 +240,6 @@
scaleAnimator.addListener(mAnimatorListener);
scaleAnimator.start();
mRunningAnimations.add(scaleAnimator);
-
- // With the delay, it could eventually animate the enter animation with no pressed state,
- // then immediately show the exit animation. If this is skipped there will be no ripple.
- if (mDelayTouchFeedback && !mPressed) {
- exitSoftware();
- }
}
private void exitSoftware() {
@@ -288,16 +253,7 @@
private void setPressedHardware(boolean pressed) {
if (pressed) {
- if (mDelayTouchFeedback) {
- if (mRunningAnimations.isEmpty()) {
- mHandler.removeCallbacksAndMessages(null);
- mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
- } else if (mVisible) {
- enterHardware();
- }
- } else {
- enterHardware();
- }
+ enterHardware();
} else {
exitHardware();
}
@@ -346,7 +302,6 @@
private void enterHardware() {
cancelAnimations();
- mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
@@ -388,12 +343,6 @@
mRunningAnimations.add(endAnim);
invalidateSelf();
-
- // With the delay, it could eventually animate the enter animation with no pressed state,
- // then immediately show the exit animation. If this is skipped there will be no ripple.
- if (mDelayTouchFeedback && !mPressed) {
- exitHardware();
- }
}
private void exitHardware() {
@@ -417,7 +366,6 @@
public void onAnimationEnd(Animator animation) {
mRunningAnimations.remove(animation);
if (mRunningAnimations.isEmpty() && !mPressed) {
- mVisible = false;
mDrawingHardwareGlow = false;
invalidateSelf();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 98bebec..e7b802d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -67,6 +67,7 @@
private int mTouchSlop;
private int mTouchDownX;
private int mTouchDownY;
+ private boolean mIsPressed;
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
@@ -79,7 +80,7 @@
private final Runnable mCheckLongPress = new Runnable() {
public void run() {
- if (isPressed()) {
+ if (mIsPressed) {
// Log.d("KeyButtonView", "longpressed: " + this);
if (isLongClickable()) {
// Just an old-fashioned ImageView
@@ -90,6 +91,12 @@
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
mLongClicked = true;
}
+
+ // Only when quick step is enabled, ripple will not be shown on touch down, then
+ // show the ripple on touch up or on long press
+ if (mLongClicked && mOverviewProxyService.getProxy() != null) {
+ setPressed(true);
+ }
}
}
};
@@ -216,7 +223,6 @@
case MotionEvent.ACTION_DOWN:
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
- setPressed(true);
mTouchDownX = (int) ev.getX();
mTouchDownY = (int) ev.getY();
if (mCode != 0) {
@@ -225,6 +231,7 @@
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
+ mIsPressed = true;
if (isProxyConnected) {
// Provide small vibration for quick step or immediate down feedback
AsyncTask.execute(() ->
@@ -232,6 +239,7 @@
.get(VibrationEffect.EFFECT_TICK, false)));
} else {
playSoundEffect(SoundEffectConstants.CLICK);
+ setPressed(mIsPressed);
}
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
@@ -242,7 +250,12 @@
boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > mTouchSlop;
boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > mTouchSlop;
if (exceededTouchSlopX || exceededTouchSlopY) {
- setPressed(false);
+ // When quick step is enabled, prevent animating the ripple triggered by
+ // setPressed and decide to run it on touch up
+ mIsPressed = false;
+ if (!isProxyConnected) {
+ setPressed(mIsPressed);
+ }
removeCallbacks(mCheckLongPress);
}
break;
@@ -254,10 +267,11 @@
removeCallbacks(mCheckLongPress);
break;
case MotionEvent.ACTION_UP:
- final boolean doIt = isPressed() && !mLongClicked;
- setPressed(false);
+ final boolean doIt = mIsPressed && !mLongClicked;
if (isProxyConnected) {
if (doIt) {
+ // Animate the ripple in on touch up with setPressed and then out later
+ setPressed(true);
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
playSoundEffect(SoundEffectConstants.CLICK);
}
@@ -266,6 +280,7 @@
// and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
+ setPressed(false);
if (mCode != 0) {
if (doIt) {
// If there was a pending remote recents animation, then we need to
@@ -295,6 +310,12 @@
mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser());
}
+ @Override
+ public void setPressed(boolean pressed) {
+ mIsPressed = pressed;
+ super.setPressed(pressed);
+ }
+
public void sendEvent(int action, int flags) {
sendEvent(action, flags, SystemClock.uptimeMillis());
}
@@ -317,7 +338,6 @@
@Override
public void abortCurrentGesture() {
setPressed(false);
- mRipple.abortDelayedRipple();
mGestureAborted = true;
}
@@ -336,7 +356,6 @@
@Override
public void setDelayTouchFeedback(boolean shouldDelay) {
- mRipple.setDelayTouchFeedback(shouldDelay);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1b55a5b..66fde79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1394,6 +1394,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = getResources().getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 943020c..18dd3c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -16,6 +16,14 @@
package com.android.systemui;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationManager;
@@ -24,17 +32,14 @@
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
import com.android.internal.messages.nano.SystemMessageProto;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForegroundServiceControllerTest extends SysuiTestCase {
@@ -49,7 +54,7 @@
}
@Test
- public void testNotificationCRUD() {
+ public void testNotificationCRUD_dungeon() {
StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, "com.example.app1");
StatusBarNotification sbn_user2_app2_fg = makeMockFgSBN(USERID_TWO, "com.example.app2");
StatusBarNotification sbn_user1_app3_fg = makeMockFgSBN(USERID_ONE, "com.example.app3");
@@ -98,6 +103,101 @@
}
@Test
+ public void testNotificationCRUD_stdLayout() {
+ StatusBarNotification sbn_user1_app1_fg =
+ makeMockFgSBN(USERID_ONE, "com.example.app1", 0, true);
+ StatusBarNotification sbn_user2_app2_fg =
+ makeMockFgSBN(USERID_TWO, "com.example.app2", 1, true);
+ StatusBarNotification sbn_user1_app3_fg =
+ makeMockFgSBN(USERID_ONE, "com.example.app3", 2, true);
+ StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
+ 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
+ StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
+ 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
+
+ assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
+ assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
+
+ // these are never added to the tracker
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
+ // should still not be there
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+
+ assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
+
+ assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
+ assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
+
+ assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
+
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+ }
+
+ @Test
+ public void testAppOpsCRUD() {
+ // no crash on remove that doesn't exist
+ fsc.onAppOpChanged(9, 1000, "pkg1", false);
+ assertNull(fsc.getAppOps(0, "pkg1"));
+
+ // multiuser & multipackage
+ fsc.onAppOpChanged(8, 50, "pkg1", true);
+ fsc.onAppOpChanged(1, 60, "pkg3", true);
+ fsc.onAppOpChanged(7, 500000, "pkg2", true);
+
+ assertEquals(1, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+
+ assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+ assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+
+ assertEquals(1, fsc.getAppOps(0, "pkg3").size());
+ assertTrue(fsc.getAppOps(0, "pkg3").contains(1));
+
+ // multiple ops for the same package
+ fsc.onAppOpChanged(9, 50, "pkg1", true);
+ fsc.onAppOpChanged(5, 50, "pkg1", true);
+
+ assertEquals(3, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(9));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+
+ assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+ assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+
+ // remove one of the multiples
+ fsc.onAppOpChanged(9, 50, "pkg1", false);
+ assertEquals(2, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+
+ // remove last op
+ fsc.onAppOpChanged(1, 60, "pkg3", false);
+ assertNull(fsc.getAppOps(0, "pkg3"));
+ }
+
+ @Test
public void testDungeonPredicate() {
StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
@@ -252,6 +352,14 @@
assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+ // importance upgrade
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+ assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+ sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1_fg,
+ NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
+
// finally, let's turn off the service
fsc.addNotification(makeMockDungeon(USERID_ONE, null),
NotificationManager.IMPORTANCE_DEFAULT);
@@ -260,12 +368,71 @@
assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
}
+ @Test
+ public void testStdLayoutBasic() {
+ final String PKG1 = "com.example.app0";
+
+ StatusBarNotification sbn_user1_app1 = makeMockFgSBN(USERID_ONE, PKG1, 0, true);
+ sbn_user1_app1.getNotification().flags = 0;
+ StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
+ // let's take out the non-fg notification and see what happens.
+ fsc.removeNotification(sbn_user1_app1);
+ // still covered by sbn_user1_app1_fg
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
+
+ // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
+ StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
+ sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
+ fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+ // ok, ok, we'll put it back
+ sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
+
+ assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
+
+ // let's try a custom layout
+ sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, false);
+ fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+ // now let's test an upgrade (non fg to fg)
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
+ sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1,
+ NotificationManager.IMPORTANCE_MIN); // this is now a fg notification
+
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+
+ // remove it, make sure we're out of compliance again
+ assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ }
+
private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
int flags) {
final Notification n = mock(Notification.class);
+ n.extras = new Bundle();
n.flags = flags;
return makeMockSBN(userid, pkg, id, tag, n);
}
+
private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
Notification n) {
final StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -278,9 +445,25 @@
when(sbn.getKey()).thenReturn("MOCK:"+userid+"|"+pkg+"|"+id+"|"+tag);
return sbn;
}
+
+ private StatusBarNotification makeMockFgSBN(int userid, String pkg, int id,
+ boolean usesStdLayout) {
+ StatusBarNotification sbn =
+ makeMockSBN(userid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE);
+ if (usesStdLayout) {
+ sbn.getNotification().contentView = null;
+ sbn.getNotification().headsUpContentView = null;
+ sbn.getNotification().bigContentView = null;
+ } else {
+ sbn.getNotification().contentView = mock(RemoteViews.class);
+ }
+ return sbn;
+ }
+
private StatusBarNotification makeMockFgSBN(int userid, String pkg) {
return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
}
+
private StatusBarNotification makeMockDungeon(int userid, String[] pkgs) {
final Notification n = mock(Notification.class);
n.flags = Notification.FLAG_ONGOING_EVENT;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
new file mode 100644
index 0000000..2a48c4b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AppOpsListenerTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private AppOpsManager mAppOpsManager;
+
+ // Dependency mocks:
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private ForegroundServiceController mFsc;
+
+ private AppOpsListener mListener;
+ private Handler mHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
+ mHandler = new Handler(Looper.getMainLooper());
+ when(mPresenter.getHandler()).thenReturn(mHandler);
+
+ mListener = new AppOpsListener(mContext);
+ }
+
+ @Test
+ public void testOnlyListenForFewOps() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+
+ verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
+ }
+
+ @Test
+ public void testStopListening() {
+ mListener.destroy();
+ verify(mAppOpsManager, times(1)).stopWatchingActive(mListener);
+ }
+
+ @Test
+ public void testInformEntryMgrOnAppOpsChange() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+ mListener.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ waitForIdleSync(mHandler);
+ verify(mEntryManager, times(1)).updateNotificationsForAppOps(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void testInformFscOnAppOpsChange() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+ mListener.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ waitForIdleSync(mHandler);
+ verify(mFsc, times(1)).onAppOpChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 544585a..ce629bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -19,10 +19,15 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -146,4 +151,34 @@
Assert.assertTrue("Should always play sounds when not trusted.",
mGroup.isSoundEffectsEnabled());
}
+
+ @Test
+ public void testShowAppOpsIcons_noHeader() {
+ // public notification is custom layout - no header
+ mGroup.setSensitive(true, true);
+ mGroup.showAppOpsIcons(new ArraySet<>());
+ }
+
+ @Test
+ public void testShowAppOpsIcons_header() throws Exception {
+ NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
+
+ NotificationContentView publicLayout = mock(NotificationContentView.class);
+ mGroup.setPublicLayout(publicLayout);
+ NotificationContentView privateLayout = mock(NotificationContentView.class);
+ mGroup.setPrivateLayout(privateLayout);
+ NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+ when(mockContainer.getNotificationChildCount()).thenReturn(1);
+ when(mockContainer.getHeaderView()).thenReturn(mockHeader);
+ mGroup.setChildrenContainer(mockContainer);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+ mGroup.showAppOpsIcons(ops);
+
+ verify(mockHeader, times(1)).showAppOpsIcons(ops);
+ verify(privateLayout, times(1)).showAppOpsIcons(ops);
+ verify(publicLayout, times(1)).showAppOpsIcons(ops);
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
index 436849c..1fb4c37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -16,14 +16,23 @@
package com.android.systemui.statusbar;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -75,4 +84,35 @@
mView.setHeadsUpAnimatingAway(true);
Assert.assertFalse(mView.isAnimatingVisibleType());
}
+
+ @Test
+ @UiThreadTest
+ public void testShowAppOpsIcons() {
+ NotificationHeaderView mockContracted = mock(NotificationHeaderView.class);
+ when(mockContracted.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockContracted);
+ NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class);
+ when(mockExpanded.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockExpanded);
+ NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class);
+ when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockHeadsUp);
+ NotificationHeaderView mockAmbient = mock(NotificationHeaderView.class);
+ when(mockAmbient.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockAmbient);
+
+ mView.setContractedChild(mockContracted);
+ mView.setExpandedChild(mockExpanded);
+ mView.setHeadsUpChild(mockHeadsUp);
+ mView.setAmbientChild(mockAmbient);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+ mView.showAppOpsIcons(ops);
+
+ verify(mockContracted, times(1)).showAppOpsIcons(ops);
+ verify(mockExpanded, times(1)).showAppOpsIcons(ops);
+ verify(mockAmbient, never()).showAppOpsIcons(ops);
+ verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 972eddb..b1e1c02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -16,8 +16,16 @@
package com.android.systemui.statusbar;
+import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
+import static android.app.AppOpsManager.OP_CAMERA;
+
+import static junit.framework.Assert.assertEquals;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -33,7 +41,9 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -41,6 +51,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -51,6 +63,10 @@
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
+ @Mock
+ ForegroundServiceController mFsc;
+ @Mock
+ NotificationData.Environment mEnvironment;
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationData mNotificationData;
@@ -58,6 +74,7 @@
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
when(mMockPackageManager.checkUidPermission(
@@ -69,9 +86,11 @@
eq(UID_ALLOW_DURING_SETUP)))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- NotificationData.Environment mock = mock(NotificationData.Environment.class);
- when(mock.getGroupManager()).thenReturn(new NotificationGroupManager());
- mNotificationData = new TestableNotificationData(mock);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
+ when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+ when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+ mNotificationData = new TestableNotificationData(mEnvironment);
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
}
@@ -117,6 +136,117 @@
Assert.assertTrue(mRow.getEntry().channel != null);
}
+ @Test
+ public void testAdd_appOpsAdded() {
+ ArraySet<Integer> expected = new ArraySet<>();
+ expected.add(3);
+ expected.add(235);
+ expected.add(1);
+ when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
+ mRow.getEntry().notification.getPackageName())).thenReturn(expected);
+
+ mNotificationData.add(mRow.getEntry());
+ assertEquals(expected.size(),
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
+ for (int op : expected) {
+ assertTrue(" entry missing op " + op,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
+ }
+ }
+
+ @Test
+ public void testAdd_noExistingAppOps() {
+ when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
+ mRow.getEntry().notification.getPackageName())).thenReturn(null);
+
+ mNotificationData.add(mRow.getEntry());
+ assertEquals(0, mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
+ }
+
+ @Test
+ public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
+ mNotificationData.add(mRow.getEntry());
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ mNotificationData.add(row2.getEntry());
+ ExpandableNotificationRow diffPkg =
+ new NotificationTestHelper(getContext()).createRow("pkg", 4000);
+ mNotificationData.add(diffPkg.getEntry());
+
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_ACCEPT_HANDOVER);
+
+ for (int op : expectedOps) {
+ mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, true);
+ }
+ for (int op : expectedOps) {
+ assertTrue(mRow.getEntry().key + " doesn't have op " + op,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
+ assertTrue(row2.getEntry().key + " doesn't have op " + op,
+ mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
+ assertFalse(diffPkg.getEntry().key + " has op " + op,
+ mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
+ }
+ }
+
+ @Test
+ public void testAppOpsRemoval() throws Exception {
+ mNotificationData.add(mRow.getEntry());
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ mNotificationData.add(row2.getEntry());
+
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_ACCEPT_HANDOVER);
+
+ for (int op : expectedOps) {
+ mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, true);
+ }
+
+ expectedOps.remove(OP_ACCEPT_HANDOVER);
+ mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, false);
+
+ assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+ assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+ assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(mRow.getEntry().key)
+ .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+ assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(row2.getEntry().key)
+ .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+ }
+
+ @Test
+ public void testSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+ }
+
+ @Test
+ public void testDoNotSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index f9ec3f92..37dd939 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -23,14 +23,17 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
@@ -274,4 +277,40 @@
assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
}
+
+ @Test
+ public void testUpdateAppOps_foregroundNoti() {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+ .thenReturn("something");
+ mEntry.row = mRow;
+ mEntryManager.getNotificationData().add(mEntry);
+
+
+ mHandler.post(() -> {
+ mEntryManager.updateNotificationsForAppOps(
+ AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
+ mEntry.notification.getPackageName(), true);
+ });
+ waitForIdleSync(mHandler);
+
+ verify(mPresenter, times(1)).updateNotificationViews();
+ assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
+ AppOpsManager.OP_CAMERA));
+ }
+
+ @Test
+ public void testUpdateAppOps_otherNoti() {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+ .thenReturn(null);
+ mHandler.post(() -> {
+ mEntryManager.updateNotificationsForAppOps(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+ });
+ waitForIdleSync(mHandler);
+
+ verify(mPresenter, never()).updateNotificationViews();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index f3c1171..2764254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -48,6 +48,8 @@
private ExpandableNotificationRow mRow;
private InflationException mException;
private HeadsUpManager mHeadsUpManager;
+ protected static final String PKG = "com.android.systemui";
+ protected static final int UID = 1000;
public NotificationTestHelper(Context context) {
mContext = context;
@@ -55,7 +57,7 @@
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
}
- public ExpandableNotificationRow createRow() throws Exception {
+ public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -67,10 +69,19 @@
.setContentText("Text")
.setPublicVersion(publicVersion)
.build();
- return createRow(notification);
+ return createRow(notification, pkg, uid);
+ }
+
+ public ExpandableNotificationRow createRow() throws Exception {
+ return createRow(PKG, UID);
}
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
+ return createRow(notification, PKG, UID);
+ }
+
+ public ExpandableNotificationRow createRow(Notification notification, String pkg, int uid)
+ throws Exception {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mInstrumentation.runOnMainSync(() -> {
@@ -83,8 +94,7 @@
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(aboveShelf -> {});
UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
- StatusBarNotification sbn = new StatusBarNotification("com.android.systemui",
- "com.android.systemui", mId++, null, 1000,
+ StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, mId++, null, uid,
2000, notification, mUser, null, System.currentTimeMillis());
NotificationData.Entry entry = new NotificationData.Entry(sbn);
entry.row = row;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index fbe730a..76ed452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -19,6 +19,9 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -170,6 +173,19 @@
assertEquals(View.VISIBLE, entry1.row.getVisibility());
}
+ @Test
+ public void testUpdateNotificationViews_appOps() throws Exception {
+ NotificationData.Entry entry0 = createEntry();
+ entry0.row = spy(entry0.row);
+ when(mNotificationData.getActiveNotifications()).thenReturn(
+ Lists.newArrayList(entry0));
+ mListContainer.addContainerView(entry0.row);
+
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(entry0.row, times(1)).showAppOpsIcons(any());
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 31442af..ff545f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -69,6 +69,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
@@ -145,6 +146,7 @@
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
+ mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class));
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index a584a7f..c22b2e7 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 915e164..401e092 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index b8e29da..f328b83 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b897c7c..7819a80 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5343,6 +5343,32 @@
// OS: P
ACTION_BATTERY_TIP_SHOWN = 1324;
+ // OPEN: Settings > Security & Location > Location > See all
+ // CATEGORY: SETTINGS
+ // OS: P
+ RECENT_LOCATION_REQUESTS_ALL = 1325;
+
+ // FIELD: The x-location of a swipe gesture, conveyed as percent of total width
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ FIELD_GESTURE_X_PERCENT = 1326;
+
+ // FIELD: The y-location of a swipe gesture, conveyed as percent of total width
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ FIELD_GESTURE_Y_PERCENT = 1327;
+
+ // ACTION: Expand the notification panel while unlocked
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ ACTION_PANEL_VIEW_EXPAND = 1328;
+
+
+ // FIELD: Rotation of the device
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ FIELD_DEVICE_ROTATION = 1329;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 7278e83..dbed242 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -590,7 +590,7 @@
}
}
- final class AnchoredWindow implements View.OnTouchListener {
+ final class AnchoredWindow {
private final @NonNull OverlayControl mOverlayControl;
private final WindowManager mWm;
private final View mContentView;
@@ -623,7 +623,6 @@
params.accessibilityTitle = mContentView.getContext()
.getString(R.string.autofill_picker_accessibility_title);
mWm.addView(mContentView, params);
- mContentView.setOnTouchListener(this);
mOverlayControl.hideOverlays();
mShowing = true;
} else {
@@ -647,7 +646,6 @@
void hide() {
try {
if (mShowing) {
- mContentView.setOnTouchListener(null);
mWm.removeView(mContentView);
mShowing = false;
}
@@ -661,16 +659,6 @@
mOverlayControl.showOverlays();
}
}
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- // When the window is touched outside, hide the window.
- if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mCallback.onCanceled();
- return true;
- }
- return false;
- }
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 62a7b8f..d17ca7f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -2191,42 +2191,42 @@
synchronized (mLock) {
final long nowRTC = System.currentTimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
- proto.write(AlarmManagerServiceProto.CURRENT_TIME, nowRTC);
- proto.write(AlarmManagerServiceProto.ELAPSED_REALTIME, nowElapsed);
- proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_CLOCK_TIME,
+ proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC);
+ proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed);
+ proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME,
mLastTimeChangeClockTime);
- proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_REALTIME,
+ proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_REALTIME,
mLastTimeChangeRealtime);
- mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
+ mConstants.dumpProto(proto, AlarmManagerServiceDumpProto.SETTINGS);
if (mAppStateTracker != null) {
mAppStateTracker.dumpProto(proto,
- AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+ AlarmManagerServiceDumpProto.FORCE_APP_STANDBY_TRACKER);
}
- proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
+ proto.write(AlarmManagerServiceDumpProto.IS_INTERACTIVE, mInteractive);
if (!mInteractive) {
// Durations
- proto.write(AlarmManagerServiceProto.TIME_SINCE_NON_INTERACTIVE_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_NON_INTERACTIVE_MS,
nowElapsed - mNonInteractiveStartTime);
- proto.write(AlarmManagerServiceProto.MAX_WAKEUP_DELAY_MS,
+ proto.write(AlarmManagerServiceDumpProto.MAX_WAKEUP_DELAY_MS,
currentNonWakeupFuzzLocked(nowElapsed));
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_DISPATCH_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_DISPATCH_MS,
nowElapsed - mLastAlarmDeliveryTime);
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
nowElapsed - mNextNonWakeupDeliveryTime);
}
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
mNextNonWakeup - nowElapsed);
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_WAKEUP_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_WAKEUP_MS,
mNextWakeup - nowElapsed);
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_MS,
nowElapsed - mLastWakeup);
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
nowElapsed - mLastWakeupSet);
- proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
+ proto.write(AlarmManagerServiceDumpProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
final TreeSet<Integer> users = new TreeSet<>();
final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
@@ -2242,14 +2242,14 @@
final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
final long time = next != null ? next.getTriggerTime() : 0;
final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
- final long aToken = proto.start(AlarmManagerServiceProto.NEXT_ALARM_CLOCK_METADATA);
+ final long aToken = proto.start(AlarmManagerServiceDumpProto.NEXT_ALARM_CLOCK_METADATA);
proto.write(AlarmClockMetadataProto.USER, user);
proto.write(AlarmClockMetadataProto.IS_PENDING_SEND, pendingSend);
proto.write(AlarmClockMetadataProto.TRIGGER_TIME_MS, time);
proto.end(aToken);
}
for (Batch b : mAlarmBatches) {
- b.writeToProto(proto, AlarmManagerServiceProto.PENDING_ALARM_BATCHES,
+ b.writeToProto(proto, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES,
nowElapsed, nowRTC);
}
for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
@@ -2257,66 +2257,66 @@
if (blockedAlarms != null) {
for (Alarm a : blockedAlarms) {
a.writeToProto(proto,
- AlarmManagerServiceProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
+ AlarmManagerServiceDumpProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
nowElapsed, nowRTC);
}
}
}
if (mPendingIdleUntil != null) {
mPendingIdleUntil.writeToProto(
- proto, AlarmManagerServiceProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
+ proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
}
for (Alarm a : mPendingWhileIdleAlarms) {
- a.writeToProto(proto, AlarmManagerServiceProto.PENDING_WHILE_IDLE_ALARMS,
+ a.writeToProto(proto, AlarmManagerServiceDumpProto.PENDING_WHILE_IDLE_ALARMS,
nowElapsed, nowRTC);
}
if (mNextWakeFromIdle != null) {
- mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceProto.NEXT_WAKE_FROM_IDLE,
+ mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceDumpProto.NEXT_WAKE_FROM_IDLE,
nowElapsed, nowRTC);
}
for (Alarm a : mPendingNonWakeupAlarms) {
- a.writeToProto(proto, AlarmManagerServiceProto.PAST_DUE_NON_WAKEUP_ALARMS,
+ a.writeToProto(proto, AlarmManagerServiceDumpProto.PAST_DUE_NON_WAKEUP_ALARMS,
nowElapsed, nowRTC);
}
- proto.write(AlarmManagerServiceProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
- proto.write(AlarmManagerServiceProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
- proto.write(AlarmManagerServiceProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
- proto.write(AlarmManagerServiceProto.MAX_NON_INTERACTIVE_DURATION_MS,
+ proto.write(AlarmManagerServiceDumpProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
+ proto.write(AlarmManagerServiceDumpProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
+ proto.write(AlarmManagerServiceDumpProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
+ proto.write(AlarmManagerServiceDumpProto.MAX_NON_INTERACTIVE_DURATION_MS,
mNonInteractiveTime);
- proto.write(AlarmManagerServiceProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
- proto.write(AlarmManagerServiceProto.PENDING_INTENT_SEND_COUNT, mSendCount);
- proto.write(AlarmManagerServiceProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
- proto.write(AlarmManagerServiceProto.LISTENER_SEND_COUNT, mListenerCount);
- proto.write(AlarmManagerServiceProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
+ proto.write(AlarmManagerServiceDumpProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
+ proto.write(AlarmManagerServiceDumpProto.PENDING_INTENT_SEND_COUNT, mSendCount);
+ proto.write(AlarmManagerServiceDumpProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
+ proto.write(AlarmManagerServiceDumpProto.LISTENER_SEND_COUNT, mListenerCount);
+ proto.write(AlarmManagerServiceDumpProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
for (InFlight f : mInFlight) {
- f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
+ f.writeToProto(proto, AlarmManagerServiceDumpProto.OUTSTANDING_DELIVERIES);
}
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
final long token = proto.start(
- AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
+ AlarmManagerServiceDumpProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID, uid);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid);
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
lastTime + getWhileIdleMinIntervalLocked(uid));
proto.end(token);
}
for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- proto.write(AlarmManagerServiceProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
+ proto.write(AlarmManagerServiceDumpProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
mUseAllowWhileIdleShortTime.keyAt(i));
}
}
- mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
+ mLog.writeToProto(proto, AlarmManagerServiceDumpProto.RECENT_PROBLEMS);
final FilterStats[] topFilters = new FilterStats[10];
final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
@@ -2357,13 +2357,13 @@
}
}
for (int i = 0; i < len; ++i) {
- final long token = proto.start(AlarmManagerServiceProto.TOP_ALARMS);
+ final long token = proto.start(AlarmManagerServiceDumpProto.TOP_ALARMS);
FilterStats fs = topFilters[i];
- proto.write(AlarmManagerServiceProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
- proto.write(AlarmManagerServiceProto.TopAlarm.PACKAGE_NAME,
+ proto.write(AlarmManagerServiceDumpProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
+ proto.write(AlarmManagerServiceDumpProto.TopAlarm.PACKAGE_NAME,
fs.mBroadcastStats.mPackageName);
- fs.writeToProto(proto, AlarmManagerServiceProto.TopAlarm.FILTER);
+ fs.writeToProto(proto, AlarmManagerServiceDumpProto.TopAlarm.FILTER);
proto.end(token);
}
@@ -2372,10 +2372,10 @@
for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
for (int ip = 0; ip < uidStats.size(); ++ip) {
- final long token = proto.start(AlarmManagerServiceProto.ALARM_STATS);
+ final long token = proto.start(AlarmManagerServiceDumpProto.ALARM_STATS);
BroadcastStats bs = uidStats.valueAt(ip);
- bs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.BROADCAST);
+ bs.writeToProto(proto, AlarmManagerServiceDumpProto.AlarmStat.BROADCAST);
// uidStats is an ArrayMap, which we can't sort.
tmpFilters.clear();
@@ -2384,7 +2384,7 @@
}
Collections.sort(tmpFilters, comparator);
for (FilterStats fs : tmpFilters) {
- fs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.FILTERS);
+ fs.writeToProto(proto, AlarmManagerServiceDumpProto.AlarmStat.FILTERS);
}
proto.end(token);
@@ -2395,7 +2395,7 @@
for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
final long token = proto.start(
- AlarmManagerServiceProto.ALLOW_WHILE_IDLE_DISPATCHES);
+ AlarmManagerServiceDumpProto.ALLOW_WHILE_IDLE_DISPATCHES);
proto.write(IdleDispatchEntryProto.UID, ent.uid);
proto.write(IdleDispatchEntryProto.PKG, ent.pkg);
@@ -2411,7 +2411,7 @@
if (WAKEUP_STATS) {
for (WakeupEvent event : mRecentWakeups) {
- final long token = proto.start(AlarmManagerServiceProto.RECENT_WAKEUP_HISTORY);
+ final long token = proto.start(AlarmManagerServiceDumpProto.RECENT_WAKEUP_HISTORY);
proto.write(WakeupEventProto.UID, event.uid);
proto.write(WakeupEventProto.ACTION, event.action);
proto.write(WakeupEventProto.WHEN, event.when);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ca67a34..53c9ecb 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -211,9 +211,11 @@
public final class ActiveCallback implements DeathRecipient {
final IAppOpsActiveCallback mCallback;
+ final int mUid;
- public ActiveCallback(IAppOpsActiveCallback callback) {
+ public ActiveCallback(IAppOpsActiveCallback callback, int uid) {
mCallback = callback;
+ mUid = uid;
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -230,24 +232,22 @@
}
}
- final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
public final class ClientState extends Binder implements DeathRecipient {
+ final ArrayList<Op> mStartedOps = new ArrayList<>();
final IBinder mAppToken;
final int mPid;
- final ArrayList<Op> mStartedOps;
public ClientState(IBinder appToken) {
mAppToken = appToken;
mPid = Binder.getCallingPid();
- if (appToken instanceof Binder) {
- // For local clients, there is no reason to track them.
- mStartedOps = null;
- } else {
- mStartedOps = new ArrayList<Op>();
+ // Watch only for remote processes dying
+ if (!(appToken instanceof Binder)) {
try {
mAppToken.linkToDeath(this, 0);
} catch (RemoteException e) {
+ /* do nothing */
}
}
}
@@ -256,7 +256,7 @@
public String toString() {
return "ClientState{" +
"mAppToken=" + mAppToken +
- ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+ ", " + "pid=" + mPid +
'}';
}
@@ -264,7 +264,7 @@
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
- finishOperationLocked(mStartedOps.get(i));
+ finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true);
}
mClients.remove(mAppToken);
}
@@ -397,6 +397,27 @@
mUidStates.remove(uid);
}
+ // Finish ops other packages started on behalf of the package.
+ final int clientCount = mClients.size();
+ for (int i = 0; i < clientCount; i++) {
+ final ClientState client = mClients.valueAt(i);
+ if (client.mStartedOps == null) {
+ continue;
+ }
+ final int opCount = client.mStartedOps.size();
+ for (int j = opCount - 1; j >= 0; j--) {
+ final Op op = client.mStartedOps.get(j);
+ if (uid == op.uid && packageName.equals(op.packageName)) {
+ finishOperationLocked(op, /*finishNested*/ true);
+ client.mStartedOps.remove(j);
+ if (op.nesting <= 0) {
+ scheduleOpActiveChangedIfNeededLocked(op.op,
+ uid, packageName, false);
+ }
+ }
+ }
+ }
+
if (ops != null) {
scheduleFastWriteLocked();
@@ -1195,8 +1216,11 @@
@Override
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
- "startWatchingActive");
+ int watchedUid = -1;
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = Binder.getCallingUid();
+ }
if (ops != null) {
Preconditions.checkArrayElementsInRange(ops, 0,
AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
@@ -1210,7 +1234,7 @@
callbacks = new SparseArray<>();
mActiveWatchers.put(callback.asBinder(), callbacks);
}
- final ActiveCallback activeCallback = new ActiveCallback(callback);
+ final ActiveCallback activeCallback = new ActiveCallback(callback, watchedUid);
for (int op : ops) {
callbacks.put(op, activeCallback);
}
@@ -1239,7 +1263,8 @@
}
@Override
- public int startOperation(IBinder token, int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid, String packageName,
+ boolean startIfModeDefault) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1265,7 +1290,8 @@
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
final int uidMode = uidState.opModes.get(switchCode);
- if (uidMode != AppOpsManager.MODE_ALLOWED) {
+ if (uidMode != AppOpsManager.MODE_ALLOWED
+ && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
@@ -1274,7 +1300,8 @@
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && (!startIfModeDefault || switchOp.mode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
@@ -1316,13 +1343,11 @@
if (op == null) {
return;
}
- if (client.mStartedOps != null) {
- if (!client.mStartedOps.remove(op)) {
- throw new IllegalStateException("Operation not started: uid" + op.uid
- + " pkg=" + op.packageName + " op=" + op.op);
- }
+ if (!client.mStartedOps.remove(op)) {
+ throw new IllegalStateException("Operation not started: uid" + op.uid
+ + " pkg=" + op.packageName + " op=" + op.op);
}
- finishOperationLocked(op);
+ finishOperationLocked(op, /*finishNested*/ false);
if (op.nesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
}
@@ -1337,6 +1362,9 @@
final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
ActiveCallback callback = callbacks.get(code);
if (callback != null) {
+ if (callback.mUid >= 0 && callback.mUid != uid) {
+ continue;
+ }
if (dispatchedCallbacks == null) {
dispatchedCallbacks = new ArraySet<>();
}
@@ -1380,9 +1408,9 @@
return AppOpsManager.permissionToOpCode(permission);
}
- void finishOperationLocked(Op op) {
- if (op.nesting <= 1) {
- if (op.nesting == 1) {
+ void finishOperationLocked(Op op, boolean finishNested) {
+ if (op.nesting <= 1 || finishNested) {
+ if (op.nesting == 1 || finishNested) {
op.duration = (int)(System.currentTimeMillis() - op.time);
op.time += op.duration;
} else {
@@ -2420,7 +2448,7 @@
pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
ClientState cs = mClients.valueAt(i);
pw.print(" "); pw.println(cs);
- if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+ if (cs.mStartedOps.size() > 0) {
pw.println(" Started ops:");
for (int j=0; j<cs.mStartedOps.size(); j++) {
Op op = cs.mStartedOps.get(j);
@@ -2651,8 +2679,12 @@
@Override
public boolean isOperationActive(int code, int uid, String packageName) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
- "isOperationActive");
+ if (Binder.getCallingUid() != uid) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
verifyIncomingOp(code);
final String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -2661,8 +2693,6 @@
synchronized (AppOpsService.this) {
for (int i = mClients.size() - 1; i >= 0; i--) {
final ClientState client = mClients.valueAt(i);
- if (client.mStartedOps == null) continue;
-
for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
final Op op = client.mStartedOps.get(j);
if (op.op == code && op.uid == uid) return true;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f633003..79297aa 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -1359,9 +1360,8 @@
if (nai != null) {
synchronized (nai) {
if (nai.networkCapabilities != null) {
- // TODO : don't remove the UIDs when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- return networkCapabilitiesWithoutUids(nai.networkCapabilities);
+ return networkCapabilitiesWithoutUidsUnlessAllowed(nai.networkCapabilities,
+ Binder.getCallingPid(), Binder.getCallingUid());
}
}
}
@@ -1374,10 +1374,18 @@
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
- private NetworkCapabilities networkCapabilitiesWithoutUids(NetworkCapabilities nc) {
+ private NetworkCapabilities networkCapabilitiesWithoutUidsUnlessAllowed(
+ NetworkCapabilities nc, int callerPid, int callerUid) {
+ if (checkSettingsPermission(callerPid, callerUid)) return new NetworkCapabilities(nc);
return new NetworkCapabilities(nc).setUids(null);
}
+ private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
+ if (!checkSettingsPermission()) {
+ nc.setSingleUid(Binder.getCallingUid());
+ }
+ }
+
@Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
@@ -1577,6 +1585,16 @@
"ConnectivityService");
}
+ private boolean checkSettingsPermission() {
+ return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_SETTINGS);
+ }
+
+ private boolean checkSettingsPermission(int pid, int uid) {
+ return PERMISSION_GRANTED == mContext.checkPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, pid, uid);
+ }
+
private void enforceTetherAccessPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -4258,13 +4276,12 @@
enforceMeteredApnPolicy(networkCapabilities);
}
ensureRequestableCapabilities(networkCapabilities);
- // Set the UID range for this request to the single UID of the requester.
+ // Set the UID range for this request to the single UID of the requester, or to an empty
+ // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
// are no visible methods to set the UIDs, an app could use reflection to try and get
// networks for other apps so it's essential that the UIDs are overwritten.
- // TODO : don't forcefully set the UID when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- networkCapabilities.setSingleUid(Binder.getCallingUid());
+ restrictRequestUidsForCaller(networkCapabilities);
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
@@ -4338,9 +4355,7 @@
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureValidNetworkSpecifier(networkCapabilities);
- // TODO : don't forcefully set the UID when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- networkCapabilities.setSingleUid(Binder.getCallingUid());
+ restrictRequestUidsForCaller(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -4394,9 +4409,7 @@
}
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
- // TODO : don't forcefully set the UIDs when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- nc.setSingleUid(Binder.getCallingUid());
+ restrictRequestUidsForCaller(nc);
if (!ConnectivityManager.checkChangePermission(mContext)) {
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
// make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
@@ -4426,9 +4439,7 @@
ensureValidNetworkSpecifier(networkCapabilities);
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
- // TODO : don't forcefully set the UIDs when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- nc.setSingleUid(Binder.getCallingUid());
+ restrictRequestUidsForCaller(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -4992,8 +5003,8 @@
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
- final NetworkCapabilities nc =
- networkCapabilitiesWithoutUids(networkAgent.networkCapabilities);
+ final NetworkCapabilities nc = networkCapabilitiesWithoutUidsUnlessAllowed(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(bundle, nc);
break;
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 119c9df..ea80ac1 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -72,7 +72,7 @@
}
}
- private boolean filterEquals(IntentFilter f1, IntentFilter f2) {
+ public static boolean filterEquals(IntentFilter f1, IntentFilter f2) {
int s1 = f1.countActions();
int s2 = f2.countActions();
if (s1 != s2) {
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 58731d2..8e9e74e 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+
import android.app.ActivityThread;
import android.content.Context;
import android.os.IBinder;
@@ -199,6 +201,9 @@
/**
* Publish the service so it is accessible to other services and apps.
+ *
+ * @param name the name of the new service
+ * @param service the service object
*/
protected final void publishBinderService(String name, IBinder service) {
publishBinderService(name, service, false);
@@ -206,10 +211,29 @@
/**
* Publish the service so it is accessible to other services and apps.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ * @param allowIsolated set to true to allow isolated sandboxed processes
+ * to access this service
*/
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated) {
- ServiceManager.addService(name, service, allowIsolated);
+ publishBinderService(name, service, allowIsolated, DUMP_FLAG_PRIORITY_DEFAULT);
+ }
+
+ /**
+ * Publish the service so it is accessible to other services and apps.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ * @param allowIsolated set to true to allow isolated sandboxed processes
+ * to access this service
+ * @param dumpPriority supported dump priority levels as a bitmask
+ */
+ protected final void publishBinderService(String name, IBinder service,
+ boolean allowIsolated, int dumpPriority) {
+ ServiceManager.addService(name, service, allowIsolated, dumpPriority);
}
/**
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 8b5176e..e202618 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -37,6 +37,7 @@
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.PreciseDisconnectCause;
@@ -179,6 +180,8 @@
private ArrayList<List<CellInfo>> mCellInfo = null;
+ private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
+
private VoLteServiceState mVoLteServiceState = new VoLteServiceState();
private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -335,6 +338,7 @@
mDataConnectionLinkProperties = new LinkProperties[numPhones];
mDataConnectionNetworkCapabilities = new NetworkCapabilities[numPhones];
mCellInfo = new ArrayList<List<CellInfo>>();
+ mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -349,6 +353,7 @@
mCallForwarding[i] = false;
mCellLocation[i] = new Bundle();
mCellInfo.add(i, null);
+ mPhysicalChannelConfigs.add(i, null);
mConnectedApns[i] = new ArrayList<String>();
}
@@ -659,6 +664,14 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
+ try {
+ r.callback.onPhysicalChannelConfigurationChanged(
+ mPhysicalChannelConfigs.get(phoneId));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -1020,6 +1033,45 @@
}
}
+ public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+ notifyPhysicalChannelConfigurationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ configs);
+ }
+
+ public void notifyPhysicalChannelConfigurationForSubscriber(int subId,
+ List<PhysicalChannelConfig> configs) {
+ if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+ }
+
+ synchronized (mRecords) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ if (validatePhoneId(phoneId)) {
+ mPhysicalChannelConfigs.set(phoneId, configs);
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ if (DBG_LOC) {
+ log("notifyPhysicalChannelConfiguration: mPhysicalChannelConfigs="
+ + configs + " r=" + r);
+ }
+ r.callback.onPhysicalChannelConfigurationChanged(configs);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@Override
public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 14c99b2..752c44a 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -98,7 +98,7 @@
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
- private final IAppOpsService mAppOpsService;
+ private final AppOpsManager mAppOps;
private final IBatteryStats mBatteryStatsService;
private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
@@ -265,8 +265,7 @@
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
- mAppOpsService =
- IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -721,17 +720,10 @@
}
private int getAppOpMode(Vibration vib) {
- int mode;
- try {
- mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
- vib.usageHint, vib.uid, vib.opPkg);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get appop mode for vibration!", e);
- mode = AppOpsManager.MODE_IGNORED;
+ int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
+ vib.usageHint, vib.uid, vib.opPkg);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
}
return mode;
}
@@ -741,11 +733,8 @@
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
try {
if (mCurrentVibration != null) {
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
- mCurrentVibration.opPkg);
- } catch (RemoteException e) { }
+ mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+ mCurrentVibration.opPkg);
unlinkVibration(mCurrentVibration);
mCurrentVibration = null;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5fc4373..0c6746e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3861,8 +3861,9 @@
return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
}
- protected void writeToProto(ProtoOutputStream proto) {
+ protected void writeToProto(ProtoOutputStream proto, long fieldId) {
synchronized (mAm) {
+ final long outterToken = proto.start(fieldId);
int[] users = mAm.mUserController.getUsers();
for (int user : users) {
ServiceMap smap = mServiceMap.get(user);
@@ -3878,6 +3879,7 @@
}
proto.end(token);
}
+ proto.end(outterToken);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f4b0dba..0de4f86 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -440,15 +440,17 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.am.proto.ActivityManagerServiceProto;
-import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpServicesProto;
import com.android.server.am.proto.GrantUriProto;
import com.android.server.am.proto.ImportanceTokenProto;
-import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.proto.MemInfoDumpProto;
import com.android.server.am.proto.NeededUriGrantsProto;
import com.android.server.am.proto.ProcessOomProto;
import com.android.server.am.proto.ProcessToGcProto;
-import com.android.server.am.proto.ProcessesProto;
-import com.android.server.am.proto.ProcessesProto.UidObserverRegistrationProto;
import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -556,6 +558,9 @@
static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ // Maximum number of receivers an app can register.
+ private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
+
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
@@ -703,6 +708,8 @@
// Whether we should use SCHED_FIFO for UI and RenderThreads.
private boolean mUseFifoUiScheduling = false;
+ private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService";
+
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
@@ -802,6 +809,7 @@
boolean asProto) {
if (asProto) return;
doDump(fd, pw, new String[]{"activities"}, asProto);
+ doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto);
}
@Override
@@ -1376,9 +1384,9 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(ProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
- proto.write(ProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
- proto.write(ProcessesProto.PendingTempWhitelist.TAG, tag);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
proto.end(token);
}
}
@@ -10147,92 +10155,33 @@
}
/**
- * Updates (grants or revokes) a persitable URI permission.
- *
- * @param uri URI to be granted or revoked.
- * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
- * applies to all URIs that are prefixed by this URI.
- * @param packageName target package.
- * @param grant if {@code true} a new permission will be granted, otherwise an existing
- * permission will be revoked.
- * @param userId user handle
- *
- * @return whether or not the requested succeeded.
- *
- * @deprecated TODO(b/72055774): caller should use takePersistableUriPermission() or
- * releasePersistableUriPermission() instead, but such change will be made in a separate CL
- * so it can be easily reverted if it breaks existing functionality.
- */
- @Deprecated // STOPSHIP if not removed
- @Override
- public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
- boolean grant, int userId) {
- enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
- "updatePersistableUriPermission");
- final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
-
- final GrantUri grantUri = new GrantUri(userId, uri, prefix);
-
- boolean persistChanged = false;
- synchronized (this) {
- if (grant) { // Grant
- final String authority = uri.getAuthority();
- final ProviderInfo pi = getProviderInfoLocked(authority, userId, 0);
- if (pi == null) {
- Slog.w(TAG, "No content provider found for authority " + authority);
- return false;
- }
- final UriPermission permission = findOrCreateUriPermissionLocked(pi.packageName,
- packageName, uid, grantUri);
- if (permission.isNew()) {
- final int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- permission.initPersistedModes(modeFlags, System.currentTimeMillis());
- persistChanged = true;
- } else {
- // Caller should not try to grant permission that is already granted.
- Slog.w(TAG_URI_PERMISSION,
- "permission already granted for " + grantUri.toSafeString());
- return false;
- }
- persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
- } else { // Revoke
- final UriPermission permission = findUriPermissionLocked(uid, grantUri);
- if (permission == null) {
- // Caller should not try to revoke permission that is not granted.
- Slog.v(TAG_URI_PERMISSION, "no permission for " + grantUri.toSafeString());
- return false;
- } else {
- permission.modeFlags = 0;
- removeUriPermissionIfNeededLocked(permission);
- persistChanged = true;
- }
- }
- if (persistChanged) {
- schedulePersistUriGrants();
- }
- }
- return true;
- }
-
- /**
* @param uri This uri must NOT contain an embedded userId.
+ * @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
+ * calling uid)
* @param userId The userId in which the uri is to be resolved.
*/
@Override
- public void takePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
- enforceNotIsolatedCaller("takePersistableUriPermission");
+ public void takePersistableUriPermission(Uri uri, final int modeFlags,
+ @Nullable String toPackage, int userId) {
+ final int uid;
+ if (toPackage != null) {
+ enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
+ "takePersistableUriPermission");
+ uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
+ } else {
+ enforceNotIsolatedCaller("takePersistableUriPermission");
+ uid = Binder.getCallingUid();
+ }
Preconditions.checkFlagsArgument(modeFlags,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
synchronized (this) {
- final int callingUid = Binder.getCallingUid();
boolean persistChanged = false;
GrantUri grantUri = new GrantUri(userId, uri, false);
- UriPermission exactPerm = findUriPermissionLocked(callingUid, grantUri);
- UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+ UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
+ UriPermission prefixPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, true));
final boolean exactValid = (exactPerm != null)
@@ -10242,7 +10191,7 @@
if (!(exactValid || prefixValid)) {
throw new SecurityException("No persistable permission grants found for UID "
- + callingUid + " and Uri " + grantUri.toSafeString());
+ + uid + " and Uri " + grantUri.toSafeString());
}
if (exactValid) {
@@ -10252,7 +10201,7 @@
persistChanged |= prefixPerm.takePersistableModes(modeFlags);
}
- persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid);
+ persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
if (persistChanged) {
schedulePersistUriGrants();
@@ -10262,25 +10211,36 @@
/**
* @param uri This uri must NOT contain an embedded userId.
+ * @param toPackage Name of the target package whose uri is being released (if {@code null},
+ * uses calling uid)
* @param userId The userId in which the uri is to be resolved.
*/
@Override
- public void releasePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
- enforceNotIsolatedCaller("releasePersistableUriPermission");
+ public void releasePersistableUriPermission(Uri uri, final int modeFlags,
+ @Nullable String toPackage, int userId) {
+
+ final int uid;
+ if (toPackage != null) {
+ enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
+ "releasePersistableUriPermission");
+ uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
+ } else {
+ enforceNotIsolatedCaller("releasePersistableUriPermission");
+ uid = Binder.getCallingUid();
+ }
Preconditions.checkFlagsArgument(modeFlags,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
synchronized (this) {
- final int callingUid = Binder.getCallingUid();
boolean persistChanged = false;
- UriPermission exactPerm = findUriPermissionLocked(callingUid,
+ UriPermission exactPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, false));
- UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+ UriPermission prefixPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, true));
- if (exactPerm == null && prefixPerm == null) {
- throw new SecurityException("No permission grants found for UID " + callingUid
+ if (exactPerm == null && prefixPerm == null && toPackage == null) {
+ throw new SecurityException("No permission grants found for UID " + uid
+ " and Uri " + uri.toSafeString());
}
@@ -12879,6 +12839,25 @@
}
@Override
+ public boolean isBackgroundRestricted(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ final int packageUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.getUserId(callingUid));
+ if (packageUid != callingUid) {
+ throw new IllegalArgumentException("Uid " + callingUid
+ + " cannot query restriction state for package " + packageName);
+ }
+ } catch (RemoteException exc) {
+ // Ignore.
+ }
+ final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ callingUid, packageName);
+ return (mode != AppOpsManager.MODE_ALLOWED);
+ }
+
+ @Override
public void backgroundWhitelistUid(final int uid) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
@@ -15754,12 +15733,12 @@
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
- // output proto is ActivityStackSupervisorProto
+ // output proto is ActivityManagerServiceDumpActivitiesProto
synchronized (this) {
writeActivitiesToProtoLocked(proto);
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
- // output proto is BroadcastProto
+ // output proto is ActivityManagerServiceDumpBroadcastsProto
synchronized (this) {
writeBroadcastsToProtoLocked(proto);
}
@@ -15781,7 +15760,8 @@
pw.println("Use -h for help.");
}
} else if ("service".equals(cmd)) {
- mServices.writeToProto(proto);
+ // output proto is ActivityManagerServiceDumpServicesProto
+ mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
@@ -15803,7 +15783,7 @@
proto.end(broadcastToken);
long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
- mServices.writeToProto(proto);
+ mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
@@ -16166,8 +16146,8 @@
}
private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
- // The output proto of "activity --proto activities" is ActivityStackSupervisorProto
- mStackSupervisor.writeToProto(proto);
+ // The output proto of "activity --proto activities" is ActivityManagerServiceDumpActivitiesProto
+ mStackSupervisor.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
private void dumpLastANRLocked(PrintWriter pw) {
@@ -16864,7 +16844,7 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
if (r.persistent) {
numPers++;
}
@@ -16876,7 +16856,7 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.ISOLATED_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS);
}
for (int i=0; i<mActiveInstrumentation.size(); i++) {
@@ -16885,7 +16865,7 @@
&& !ai.mTargetInfo.packageName.equals(dumpPackage)) {
continue;
}
- ai.writeToProto(proto, ProcessesProto.ACTIVE_INSTRUMENTATIONS);
+ ai.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
}
int whichAppId = getAppId(dumpPackage);
@@ -16894,7 +16874,7 @@
if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
continue;
}
- uidRec.writeToProto(proto, ProcessesProto.ACTIVE_UIDS);
+ uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
}
for (int i=0; i<mValidateUids.size(); i++) {
@@ -16902,16 +16882,16 @@
if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
continue;
}
- uidRec.writeToProto(proto, ProcessesProto.VALIDATE_UIDS);
+ uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
}
if (mLruProcesses.size() > 0) {
- long lruToken = proto.start(ProcessesProto.LRU_PROCS);
+ long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
int total = mLruProcesses.size();
- proto.write(ProcessesProto.LruProcesses.SIZE, total);
- proto.write(ProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
- proto.write(ProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
- writeProcessOomListToProto(proto, ProcessesProto.LruProcesses.LIST, this,
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
+ writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
mLruProcesses,false, dumpPackage);
proto.end(lruToken);
}
@@ -16923,7 +16903,7 @@
if (!r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PIDS_SELF_LOCKED);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
}
}
}
@@ -16937,7 +16917,7 @@
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
- it.writeToProto(proto, ProcessesProto.IMPORTANT_PROCS);
+ it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
}
}
}
@@ -16947,7 +16927,7 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PERSISTENT_STARTING_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
}
for (int i=0; i<mRemovedProcesses.size(); i++) {
@@ -16955,7 +16935,7 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.REMOVED_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.REMOVED_PROCS);
}
for (int i=0; i<mProcessesOnHold.size(); i++) {
@@ -16963,41 +16943,41 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.ON_HOLD_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS);
}
- writeProcessesToGcToProto(proto, ProcessesProto.GC_PROCS, dumpPackage);
- mAppErrors.writeToProto(proto, ProcessesProto.APP_ERRORS, dumpPackage);
+ writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
+ mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
if (dumpPackage == null) {
- mUserController.writeToProto(proto, ProcessesProto.USER_CONTROLLER);
- getGlobalConfiguration().writeToProto(proto, ProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+ mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
+ getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
}
if (mHomeProcess != null && (dumpPackage == null
|| mHomeProcess.pkgList.containsKey(dumpPackage))) {
- mHomeProcess.writeToProto(proto, ProcessesProto.HOME_PROC);
+ mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
}
if (mPreviousProcess != null && (dumpPackage == null
|| mPreviousProcess.pkgList.containsKey(dumpPackage))) {
- mPreviousProcess.writeToProto(proto, ProcessesProto.PREVIOUS_PROC);
- proto.write(ProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
|| mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
- mHeavyWeightProcess.writeToProto(proto, ProcessesProto.HEAVY_WEIGHT_PROC);
+ mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
}
for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage == null || dumpPackage.equals(pkg)) {
- long compatToken = proto.start(ProcessesProto.SCREEN_COMPAT_PACKAGES);
- proto.write(ProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
- proto.write(ProcessesProto.ScreenCompatPackage.MODE, mode);
+ long compatToken = proto.start(ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE, mode);
proto.end(compatToken);
}
}
@@ -17007,89 +16987,89 @@
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
- reg.writeToProto(proto, ProcessesProto.UID_OBSERVERS);
+ reg.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
for (int v : mDeviceIdleWhitelist) {
- proto.write(ProcessesProto.DEVICE_IDLE_WHITELIST, v);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v);
}
for (int v : mDeviceIdleTempWhitelist) {
- proto.write(ProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
}
if (mPendingTempWhitelist.size() > 0) {
for (int i=0; i < mPendingTempWhitelist.size(); i++) {
mPendingTempWhitelist.valueAt(i).writeToProto(proto,
- ProcessesProto.PENDING_TEMP_WHITELIST);
+ ActivityManagerServiceDumpProcessesProto.PENDING_TEMP_WHITELIST);
}
}
if (dumpPackage == null) {
- final long sleepToken = proto.start(ProcessesProto.SLEEP_STATUS);
- proto.write(ProcessesProto.SleepStatus.WAKEFULNESS,
+ final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
for (SleepToken st : mStackSupervisor.mSleepTokens) {
- proto.write(ProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
}
- proto.write(ProcessesProto.SleepStatus.SLEEPING, mSleeping);
- proto.write(ProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
- proto.write(ProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
proto.end(sleepToken);
if (mRunningVoice != null) {
- final long vrToken = proto.start(ProcessesProto.RUNNING_VOICE);
- proto.write(ProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
- mVoiceWakeLock.writeToProto(proto, ProcessesProto.VoiceProto.WAKELOCK);
+ final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
+ mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VoiceProto.WAKELOCK);
proto.end(vrToken);
}
- mVrController.writeToProto(proto, ProcessesProto.VR_CONTROLLER);
+ mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
|| dumpPackage.equals(mOrigDebugApp)) {
- final long debugAppToken = proto.start(ProcessesProto.DEBUG);
- proto.write(ProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
- proto.write(ProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
- proto.write(ProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
- proto.write(ProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
+ final long debugAppToken = proto.start(ActivityManagerServiceDumpProcessesProto.DEBUG);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
proto.end(debugAppToken);
}
}
if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.writeToProto(proto, ProcessesProto.CURRENT_TRACKER, true);
+ mCurAppTimeTracker.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
}
if (mMemWatchProcesses.getMap().size() > 0) {
- final long token = proto.start(ProcessesProto.MEM_WATCH_PROCESSES);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
for (int i=0; i<procs.size(); i++) {
final String proc = procs.keyAt(i);
final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
- final long ptoken = proto.start(ProcessesProto.MemWatchProcess.PROCS);
- proto.write(ProcessesProto.MemWatchProcess.Process.NAME, proc);
+ final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc);
for (int j=0; j<uids.size(); j++) {
- final long utoken = proto.start(ProcessesProto.MemWatchProcess.Process.MEM_STATS);
+ final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MEM_STATS);
Pair<Long, String> val = uids.valueAt(j);
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
DebugUtils.sizeValueToString(val.first, new StringBuilder()));
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
proto.end(utoken);
}
proto.end(ptoken);
}
- final long dtoken = proto.start(ProcessesProto.MemWatchProcess.DUMP);
- proto.write(ProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
- proto.write(ProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
- proto.write(ProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
- proto.write(ProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
+ final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
proto.end(dtoken);
proto.end(token);
@@ -17097,58 +17077,58 @@
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
- proto.write(ProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
}
}
if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
(mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
- final long token = proto.start(ProcessesProto.PROFILE);
- proto.write(ProcessesProto.Profile.APP_NAME, mProfileApp);
- mProfileProc.writeToProto(proto,ProcessesProto.Profile.PROC);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME, mProfileApp);
+ mProfileProc.writeToProto(proto,ActivityManagerServiceDumpProcessesProto.Profile.PROC);
if (mProfilerInfo != null) {
- mProfilerInfo.writeToProto(proto, ProcessesProto.Profile.INFO);
- proto.write(ProcessesProto.Profile.TYPE, mProfileType);
+ mProfilerInfo.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Profile.INFO);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE, mProfileType);
}
proto.end(token);
}
}
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
- proto.write(ProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
}
if (dumpPackage == null) {
- proto.write(ProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
if (mController != null) {
- final long token = proto.start(ProcessesProto.CONTROLLER);
- proto.write(ProcessesProto.Controller.CONTROLLER, mController.toString());
- proto.write(ProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
proto.end(token);
}
- proto.write(ProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
- proto.write(ProcessesProto.PROCESSES_READY, mProcessesReady);
- proto.write(ProcessesProto.SYSTEM_READY, mSystemReady);
- proto.write(ProcessesProto.BOOTED, mBooted);
- proto.write(ProcessesProto.FACTORY_TEST, mFactoryTest);
- proto.write(ProcessesProto.BOOTING, mBooting);
- proto.write(ProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
- proto.write(ProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
- proto.write(ProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
- mStackSupervisor.mGoingToSleep.writeToProto(proto, ProcessesProto.GOING_TO_SLEEP);
- mStackSupervisor.mLaunchingActivity.writeToProto(proto, ProcessesProto.LAUNCHING_ACTIVITY);
- proto.write(ProcessesProto.ADJ_SEQ, mAdjSeq);
- proto.write(ProcessesProto.LRU_SEQ, mLruSeq);
- proto.write(ProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
- proto.write(ProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
- proto.write(ProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
- proto.write(ProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
- proto.write(ProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
- proto.write(ProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+ proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PROCESSES_READY, mProcessesReady);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SYSTEM_READY, mSystemReady);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOTED, mBooted);
+ proto.write(ActivityManagerServiceDumpProcessesProto.FACTORY_TEST, mFactoryTest);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOTING, mBooting);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
+ mStackSupervisor.mGoingToSleep.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP);
+ mStackSupervisor.mLaunchingActivity.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mLruSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
long now = SystemClock.uptimeMillis();
- ProtoUtils.toDuration(proto, ProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
- proto.write(ProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
+ ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
}
}
@@ -17458,15 +17438,15 @@
Iterator it = mRegisteredReceivers.values().iterator();
while (it.hasNext()) {
ReceiverList r = (ReceiverList)it.next();
- r.writeToProto(proto, BroadcastProto.RECEIVER_LIST);
+ r.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_LIST);
}
}
- mReceiverResolver.writeToProto(proto, BroadcastProto.RECEIVER_RESOLVER);
+ mReceiverResolver.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
for (BroadcastQueue q : mBroadcastQueues) {
- q.writeToProto(proto, BroadcastProto.BROADCAST_QUEUE);
+ q.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
}
for (int user=0; user<mStickyBroadcasts.size(); user++) {
- long token = proto.start(BroadcastProto.STICKY_BROADCASTS);
+ long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
for (Map.Entry<String, ArrayList<Intent>> ent
: mStickyBroadcasts.valueAt(user).entrySet()) {
@@ -17481,9 +17461,10 @@
proto.end(token);
}
- long handlerToken = proto.start(BroadcastProto.HANDLER);
- proto.write(BroadcastProto.MainHandler.HANDLER, mHandler.toString());
- mHandler.getLooper().writeToProto(proto, BroadcastProto.MainHandler.LOOPER);
+ long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
+ proto.write(ActivityManagerServiceDumpBroadcastsProto.MainHandler.HANDLER, mHandler.toString());
+ mHandler.getLooper().writeToProto(proto,
+ ActivityManagerServiceDumpBroadcastsProto.MainHandler.LOOPER);
proto.end(handlerToken);
}
@@ -18245,17 +18226,17 @@
MemItem mi = items.get(i);
final long token = proto.start(fieldId);
- proto.write(MemInfoProto.MemItem.TAG, tag);
- proto.write(MemInfoProto.MemItem.LABEL, mi.shortLabel);
- proto.write(MemInfoProto.MemItem.IS_PROC, mi.isProc);
- proto.write(MemInfoProto.MemItem.ID, mi.id);
- proto.write(MemInfoProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
- proto.write(MemInfoProto.MemItem.PSS_KB, mi.pss);
+ proto.write(MemInfoDumpProto.MemItem.TAG, tag);
+ proto.write(MemInfoDumpProto.MemItem.LABEL, mi.shortLabel);
+ proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc);
+ proto.write(MemInfoDumpProto.MemItem.ID, mi.id);
+ proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
+ proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
if (dumpSwapPss) {
- proto.write(MemInfoProto.MemItem.SWAP_PSS_KB, mi.swapPss);
+ proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss);
}
if (mi.subitems != null) {
- dumpMemItems(proto, MemInfoProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems,
+ dumpMemItems(proto, MemInfoDumpProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems,
true, dumpSwapPss);
}
proto.end(token);
@@ -19009,16 +18990,16 @@
if (nativeProcs.size() > 0) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
- proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
- proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+ proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
+ proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
Debug.MemoryInfo mi = null;
for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
final ProcessCpuTracker.Stats r = nativeProcs.get(i);
final int pid = r.pid;
- final long nToken = proto.start(MemInfoProto.NATIVE_PROCESSES);
+ final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
- proto.write(MemInfoProto.ProcessMemory.PID, pid);
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.baseName);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
if (mi == null) {
mi = new Debug.MemoryInfo();
@@ -19050,8 +19031,8 @@
ProtoOutputStream proto = new ProtoOutputStream(fd);
- proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
- proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+ proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
+ proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
@@ -19116,10 +19097,10 @@
}
if (opts.dumpDetails) {
if (opts.localOnly) {
- final long aToken = proto.start(MemInfoProto.APP_PROCESSES);
- final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
- proto.write(MemInfoProto.ProcessMemory.PID, pid);
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.processName);
+ final long aToken = proto.start(MemInfoDumpProto.APP_PROCESSES);
+ final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.processName);
ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
proto.end(mToken);
@@ -19131,7 +19112,7 @@
thread.dumpMemInfoProto(tp.getWriteFd(),
mi, opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
opts.dumpUnreachable, innerArgs);
- proto.write(MemInfoProto.APP_PROCESSES, tp.get());
+ proto.write(MemInfoDumpProto.APP_PROCESSES, tp.get());
} finally {
tp.kill();
}
@@ -19319,13 +19300,13 @@
opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
if (!opts.oomOnly) {
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_PROCESS, "proc",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_PROCESS, "proc",
procMems, true, opts.dumpSwapPss);
}
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom",
oomMems, false, opts.dumpSwapPss);
if (!brief && !opts.oomOnly) {
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_CATEGORY, "cat",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_CATEGORY, "cat",
catMems, true, opts.dumpSwapPss);
}
MemInfoReader memInfo = new MemInfoReader();
@@ -19343,40 +19324,40 @@
}
}
if (!brief) {
- proto.write(MemInfoProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
- proto.write(MemInfoProto.STATUS, mLastMemoryLevel);
- proto.write(MemInfoProto.CACHED_PSS_KB, cachedPss);
- proto.write(MemInfoProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
- proto.write(MemInfoProto.FREE_KB, memInfo.getFreeSizeKb());
+ proto.write(MemInfoDumpProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
+ proto.write(MemInfoDumpProto.STATUS, mLastMemoryLevel);
+ proto.write(MemInfoDumpProto.CACHED_PSS_KB, cachedPss);
+ proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
+ proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb());
}
long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
- proto.write(MemInfoProto.USED_PSS_KB, totalPss - cachedPss);
- proto.write(MemInfoProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
- proto.write(MemInfoProto.LOST_RAM_KB, lostRAM);
+ proto.write(MemInfoDumpProto.USED_PSS_KB, totalPss - cachedPss);
+ proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
+ proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM);
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
- proto.write(MemInfoProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb());
- proto.write(MemInfoProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB,
+ proto.write(MemInfoDumpProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb());
+ proto.write(MemInfoDumpProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB,
memInfo.getSwapTotalSizeKb() - memInfo.getSwapFreeSizeKb());
- proto.write(MemInfoProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb());
+ proto.write(MemInfoDumpProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb());
}
final long[] ksm = getKsmInfo();
- proto.write(MemInfoProto.KSM_SHARING_KB, ksm[KSM_SHARING]);
- proto.write(MemInfoProto.KSM_SHARED_KB, ksm[KSM_SHARED]);
- proto.write(MemInfoProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]);
- proto.write(MemInfoProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]);
+ proto.write(MemInfoDumpProto.KSM_SHARING_KB, ksm[KSM_SHARING]);
+ proto.write(MemInfoDumpProto.KSM_SHARED_KB, ksm[KSM_SHARED]);
+ proto.write(MemInfoDumpProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]);
+ proto.write(MemInfoDumpProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]);
- proto.write(MemInfoProto.TUNING_MB, ActivityManager.staticGetMemoryClass());
- proto.write(MemInfoProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass());
- proto.write(MemInfoProto.OOM_KB,
+ proto.write(MemInfoDumpProto.TUNING_MB, ActivityManager.staticGetMemoryClass());
+ proto.write(MemInfoDumpProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass());
+ proto.write(MemInfoDumpProto.OOM_KB,
mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024);
- proto.write(MemInfoProto.RESTORE_LIMIT_KB,
+ proto.write(MemInfoDumpProto.RESTORE_LIMIT_KB,
mProcessList.getCachedRestoreThresholdKb());
- proto.write(MemInfoProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic());
- proto.write(MemInfoProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx());
+ proto.write(MemInfoDumpProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic());
+ proto.write(MemInfoDumpProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx());
}
}
@@ -20564,6 +20545,12 @@
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
+ final int totalReceiversForApp = rl.app.receivers.size();
+ if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
+ throw new IllegalStateException("Too many receivers, total of "
+ + totalReceiversForApp + ", registered for pid: "
+ + rl.pid + ", callerPackage: " + callerPackage);
+ }
rl.app.receivers.add(rl);
} else {
try {
@@ -20592,11 +20579,18 @@
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
- rl.add(bf);
- if (!bf.debugCheck()) {
- Slog.w(TAG, "==> For Dynamic broadcast");
+ if (rl.containsFilter(filter)) {
+ // STOPSHIP: To track if apps are doing this a lot for b/70677313. Change to Slog.w
+ Slog.wtf(TAG, "Receiver with filter " + filter
+ + " already registered for pid " + rl.pid
+ + ", callerPackage is " + callerPackage);
+ } else {
+ rl.add(bf);
+ if (!bf.debugCheck()) {
+ Slog.w(TAG, "==> For Dynamic broadcast");
+ }
+ mReceiverResolver.addFilter(bf);
}
- mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3d7c9d2..81dae39 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -51,6 +51,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.opengl.GLES10;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -82,16 +83,18 @@
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.opengles.GL;
-import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
@@ -1858,6 +1861,137 @@
}
}
+ /**
+ * Adds all supported GL extensions for a provided EGLConfig to a set by creating an EGLContext
+ * and EGLSurface and querying extensions.
+ *
+ * @param egl An EGL API object
+ * @param display An EGLDisplay to create a context and surface with
+ * @param config The EGLConfig to get the extensions for
+ * @param surfaceSize eglCreatePbufferSurface generic parameters
+ * @param contextAttribs eglCreateContext generic parameters
+ * @param glExtensions A Set<String> to add GL extensions to
+ */
+ private static void addExtensionsForConfig(
+ EGL10 egl,
+ EGLDisplay display,
+ EGLConfig config,
+ int[] surfaceSize,
+ int[] contextAttribs,
+ Set<String> glExtensions) {
+ // Create a context.
+ EGLContext context =
+ egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, contextAttribs);
+ // No-op if we can't create a context.
+ if (context == EGL10.EGL_NO_CONTEXT) {
+ return;
+ }
+
+ // Create a surface.
+ EGLSurface surface = egl.eglCreatePbufferSurface(display, config, surfaceSize);
+ if (surface == EGL10.EGL_NO_SURFACE) {
+ egl.eglDestroyContext(display, context);
+ return;
+ }
+
+ // Update the current surface and context.
+ egl.eglMakeCurrent(display, surface, surface, context);
+
+ // Get the list of extensions.
+ String extensionList = GLES10.glGetString(GLES10.GL_EXTENSIONS);
+ if (!TextUtils.isEmpty(extensionList)) {
+ // The list of extensions comes from the driver separated by spaces.
+ // Split them apart and add them into a Set for deduping purposes.
+ for (String extension : extensionList.split(" ")) {
+ glExtensions.add(extension);
+ }
+ }
+
+ // Tear down the context and surface for this config.
+ egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ egl.eglDestroySurface(display, surface);
+ egl.eglDestroyContext(display, context);
+ }
+
+
+ Set<String> getGlExtensionsFromDriver() {
+ Set<String> glExtensions = new HashSet<>();
+
+ // Get the EGL implementation.
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ if (egl == null) {
+ getErrPrintWriter().println("Warning: couldn't get EGL");
+ return glExtensions;
+ }
+
+ // Get the default display and initialize it.
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ int[] version = new int[2];
+ egl.eglInitialize(display, version);
+
+ // Call getConfigs() in order to find out how many there are.
+ int[] numConfigs = new int[1];
+ if (!egl.eglGetConfigs(display, null, 0, numConfigs)) {
+ getErrPrintWriter().println("Warning: couldn't get EGL config count");
+ return glExtensions;
+ }
+
+ // Allocate space for all configs and ask again.
+ EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+ if (!egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) {
+ getErrPrintWriter().println("Warning: couldn't get EGL configs");
+ return glExtensions;
+ }
+
+ // Allocate surface size parameters outside of the main loop to cut down
+ // on GC thrashing. 1x1 is enough since we are only using it to get at
+ // the list of extensions.
+ int[] surfaceSize =
+ new int[] {
+ EGL10.EGL_WIDTH, 1,
+ EGL10.EGL_HEIGHT, 1,
+ EGL10.EGL_NONE
+ };
+
+ // For when we need to create a GLES2.0 context.
+ final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ int[] gles2 = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
+
+ // For getting return values from eglGetConfigAttrib
+ int[] attrib = new int[1];
+
+ for (int i = 0; i < numConfigs[0]; i++) {
+ // Get caveat for this config in order to skip slow (i.e. software) configs.
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_CAVEAT, attrib);
+ if (attrib[0] == EGL10.EGL_SLOW_CONFIG) {
+ continue;
+ }
+
+ // If the config does not support pbuffers we cannot do an eglMakeCurrent
+ // on it in addExtensionsForConfig(), so skip it here. Attempting to make
+ // it current with a pbuffer will result in an EGL_BAD_MATCH error
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_SURFACE_TYPE, attrib);
+ if ((attrib[0] & EGL10.EGL_PBUFFER_BIT) == 0) {
+ continue;
+ }
+
+ final int EGL_OPENGL_ES_BIT = 0x0001;
+ final int EGL_OPENGL_ES2_BIT = 0x0004;
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_RENDERABLE_TYPE, attrib);
+ if ((attrib[0] & EGL_OPENGL_ES_BIT) != 0) {
+ addExtensionsForConfig(egl, display, configs[i], surfaceSize, null, glExtensions);
+ }
+ if ((attrib[0] & EGL_OPENGL_ES2_BIT) != 0) {
+ addExtensionsForConfig(egl, display, configs[i], surfaceSize, gles2, glExtensions);
+ }
+ }
+
+ // Release all EGL resources.
+ egl.eglTerminate(display);
+
+ return glExtensions;
+ }
+
private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
PrintWriter pw, Configuration config, DisplayManager dm) {
Point stableSize = dm.getStableDisplaySize();
@@ -1906,18 +2040,24 @@
}
}
- /*
- GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
- protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
- gl.glGetString(GL10.GL_VERSION));
- String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
- for (String ext : glExtensions.split(" ")) {
- protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
+ Set<String> glExtensionsSet = getGlExtensionsFromDriver();
+ String[] glExtensions = new String[glExtensionsSet.size()];
+ glExtensions = glExtensionsSet.toArray(glExtensions);
+ Arrays.sort(glExtensions);
+ for (int i = 0; i < glExtensions.length; i++) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS,
+ glExtensions[i]);
+ }
+ if (pw != null) {
+ pw.print("opengl-extensions: "); pw.println(glExtensions[i]);
+ }
+
}
- */
PackageManager pm = mInternal.mContext.getPackageManager();
List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
+ Collections.sort(slibs, Comparator.comparing(SharedLibraryInfo::getName));
for (int i = 0; i < slibs.size(); i++) {
if (protoOutputStream != null) {
protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
@@ -1929,6 +2069,8 @@
}
FeatureInfo[] features = pm.getSystemAvailableFeatures();
+ Arrays.sort(features, (o1, o2) ->
+ (o1.name == o2.name ? 0 : (o1.name == null ? -1 : o1.name.compareTo(o2.name))));
for (int i = 0; i < features.length; i++) {
if (features[i].name != null) {
if (protoOutputStream != null) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 274a4b0..519024a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -230,6 +230,8 @@
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
+ private static final int MAX_STORED_STATE_TRANSITIONS = 5;
+
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
AppWindowContainerController mWindowContainerController;
@@ -366,6 +368,28 @@
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final ArrayList<StateTransition> mRecentTransitions = new ArrayList<>();
+
+ // TODO(b/71506345): Remove once issue has been resolved.
+ private static class StateTransition {
+ final long time;
+ final ActivityState prev;
+ final ActivityState state;
+ final String reason;
+
+ StateTransition(ActivityState prev, ActivityState state, String reason) {
+ time = System.currentTimeMillis();
+ this.prev = prev;
+ this.state = state;
+ this.reason = reason;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + prev + "->" + state + ":" + reason + "@" + time + "]";
+ }
+ }
+
private static String startingWindowStateToString(int state) {
switch (state) {
case STARTING_WINDOW_NOT_SHOWN:
@@ -380,9 +404,18 @@
}
String getLifecycleDescription(String reason) {
+ StringBuilder transitionBuilder = new StringBuilder();
+
+ for (int i = 0, size = mRecentTransitions.size(); i < size; ++i) {
+ transitionBuilder.append(mRecentTransitions.get(i));
+ if (i + 1 < size) {
+ transitionBuilder.append(",");
+ }
+ }
+
return "name= " + this + ", component=" + intent.getComponent().flattenToShortString()
+ ", package=" + packageName + ", state=" + mState + ", reason=" + reason
- + ", time=" + System.currentTimeMillis();
+ + ", time=" + System.currentTimeMillis() + " transitions=" + transitionBuilder;
}
void dump(PrintWriter pw, String prefix) {
@@ -1581,7 +1614,36 @@
void setState(ActivityState state, String reason) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
+ " to:" + state + " reason:" + reason);
+
+ final ActivityState prev = mState;
+ final boolean stateChanged = prev != state;
+
mState = state;
+
+ if (stateChanged) {
+ if (mRecentTransitions.size() == MAX_STORED_STATE_TRANSITIONS) {
+ mRecentTransitions.remove(0);
+ }
+
+ mRecentTransitions.add(new StateTransition(prev, state, reason));
+ }
+
+ if (stateChanged && isState(DESTROYING, DESTROYED)) {
+ makeFinishingLocked();
+
+ // When moving to the destroyed state, immediately destroy the activity in the
+ // associated stack. Most paths for finishing an activity will handle an activity's path
+ // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities.
+ // However, moving to the destroyed state directly (as in the case of an app dying) and
+ // marking it as finished will lead to cleanup steps that will prevent later handling
+ // from happening.
+ if (isState(DESTROYED)) {
+ final ActivityStack stack = getStack();
+ if (stack != null) {
+ stack.activityDestroyedLocked(this, reason);
+ }
+ }
+ }
}
ActivityState getState() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2f6afd2..9276abe 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -109,6 +109,7 @@
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.servertransaction.DestroyActivityItem;
@@ -988,13 +989,32 @@
return;
}
+ final ActivityDisplay display = getDisplay();
+
+ if (inSplitScreenSecondaryWindowingMode()) {
+ // If the stack is in split-screen seconardy mode, we need to make sure we move the
+ // primary split-screen stack forward in the case it is currently behind a fullscreen
+ // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
+ // cutting between them.
+ // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
+ final ActivityStack topFullScreenStack =
+ display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ if (topFullScreenStack != null) {
+ final ActivityStack primarySplitScreenStack = display.getSplitScreenPrimaryStack();
+ if (display.getIndexOf(topFullScreenStack)
+ > display.getIndexOf(primarySplitScreenStack)) {
+ primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
+ }
+ }
+ }
+
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
}
- getDisplay().positionChildAtTop(this);
+ display.positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
insertTaskAtTop(task, null);
@@ -2609,6 +2629,8 @@
}
try {
+ final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
+ next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -2616,15 +2638,13 @@
if (!next.finishing && N > 0) {
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Delivering results to " + next + ": " + a);
- mService.mLifecycleManager.scheduleTransaction(next.app.thread,
- next.appToken, ActivityResultItem.obtain(a));
+ transaction.addCallback(ActivityResultItem.obtain(a));
}
}
if (next.newIntents != null) {
- mService.mLifecycleManager.scheduleTransaction(next.app.thread,
- next.appToken, NewIntentItem.obtain(next.newIntents,
- false /* andPause */));
+ transaction.addCallback(NewIntentItem.obtain(next.newIntents,
+ false /* andPause */));
}
// Well the app will no longer be stopped.
@@ -2641,11 +2661,13 @@
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
- mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
+
+ transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.repProcState,
mService.isNextTransitionForward())
.setDescription(next.getLifecycleDescription(
"resumeTopActivityInnerLocked")));
+ mService.mLifecycleManager.scheduleTransaction(transaction);
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
@@ -3978,16 +4000,26 @@
* state to destroy so only the cleanup here is needed.
*
* Note: Call before #removeActivityFromHistoryLocked.
+ *
+ * @param r The {@link ActivityRecord} to cleanup.
+ * @param cleanServices Whether services bound to the {@link ActivityRecord} should also be
+ * cleaned up.
+ * @param destroy Whether the {@link ActivityRecord} should be destroyed.
+ * @param clearProcess Whether the client process should be cleared.
*/
- private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
+ private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean destroy,
+ boolean clearProcess) {
onActivityRemovedFromStack(r);
r.deferRelaunchUntilPaused = false;
r.frozenBeforeDestroy = false;
- if (setState) {
+ if (destroy) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
r.setState(DESTROYED, "cleanupActivityLocked");
+ }
+
+ if (clearProcess) {
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
r.app = null;
}
@@ -4206,7 +4238,8 @@
boolean removedFromHistory = false;
- cleanUpActivityLocked(r, false, false);
+ cleanUpActivityLocked(r, false /* cleanServices */, false /* destroy */,
+ false /*clearProcess*/);
final boolean hadApp = r.app != null;
@@ -4312,7 +4345,8 @@
if (isInStackLocked(record) != null) {
if (record.isState(DESTROYING, DESTROYED)) {
- cleanUpActivityLocked(record, true, false);
+ cleanUpActivityLocked(record, true /* cleanServices */, false /* destroy */,
+ false /*clearProcess*/);
removeActivityFromHistoryLocked(record, reason);
}
}
@@ -4417,7 +4451,8 @@
r.icicle = null;
}
}
- cleanUpActivityLocked(r, true, true);
+ cleanUpActivityLocked(r, true /* cleanServices */, remove /* destroy */,
+ true /*clearProcess*/);
if (remove) {
removeActivityFromHistoryLocked(r, "appDied");
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0157c7c..26ffe95 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3777,7 +3777,8 @@
mService.mLockTaskController.dump(pw, prefix);
}
- public void writeToProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
@@ -3795,6 +3796,7 @@
}
proto.write(IS_HOME_RECENTS_COMPONENT,
mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ proto.end(token);
}
/**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dd2358c..4541acd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1041,7 +1041,9 @@
return;
}
- mStats.updateBluetoothStateLocked(info);
+ synchronized (mStats) {
+ mStats.updateBluetoothStateLocked(info);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java
index a989063..eee924f 100644
--- a/services/core/java/com/android/server/am/ReceiverList.java
+++ b/services/core/java/com/android/server/am/ReceiverList.java
@@ -17,11 +17,14 @@
package com.android.server.am;
import android.content.IIntentReceiver;
+import android.content.IntentFilter;
import android.os.Binder;
import android.os.IBinder;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
+
+import com.android.server.IntentResolver;
import com.android.server.am.proto.ReceiverListProto;
import java.io.PrintWriter;
@@ -67,6 +70,17 @@
owner.unregisterReceiver(receiver);
}
+ public boolean containsFilter(IntentFilter filter) {
+ final int N = size();
+ for (int i = 0; i < N; i++) {
+ final BroadcastFilter f = get(i);
+ if (IntentResolver.filterEquals(f, filter)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
app.writeToProto(proto, ReceiverListProto.APP);
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 6dcf041..4b1594c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -134,6 +134,7 @@
// Fetch all the surface controls and pass them to the client to get the animation
// started
+ mWindowManager.cancelRecentsAnimation();
mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
display.mDisplayId);
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 3bf1cf4..1e071aa 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -124,10 +124,6 @@
updateModeFlags();
}
- boolean isNew() {
- return persistedCreateTime == INVALID_TIME;
- }
-
void grantModes(int modeFlags, UriPermissionOwner owner) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index d32db7e..9d34a80 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -24,7 +24,7 @@
import android.util.proto.ProtoUtils;
import com.android.server.LocalServices;
-import com.android.server.am.proto.ProcessesProto.VrControllerProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.VrControllerProto;
import com.android.server.vr.VrManagerInternal;
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ca22820..8eb8058 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7039,26 +7039,75 @@
// TODO implement clearing mix attribute matching info in native audio policy
}
- public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
- if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
- + " policy " + pcb.asBinder());
- // error handling
- boolean hasPermissionForPolicy =
+ /**
+ * Checks whether caller has MODIFY_AUDIO_ROUTING permission, and the policy is registered.
+ * @param errorMsg log warning if permission check failed.
+ * @return null if the operation on the audio mixes should be cancelled.
+ */
+ @GuardedBy("mAudioPolicies")
+ private AudioPolicyProxy checkUpdateForPolicy(IAudioPolicyCallback pcb, String errorMsg) {
+ // permission check
+ final boolean hasPermissionForPolicy =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
android.Manifest.permission.MODIFY_AUDIO_ROUTING));
if (!hasPermissionForPolicy) {
- Slog.w(TAG, "Cannot change audio policy ducking handling for pid " +
+ Slog.w(TAG, errorMsg + " for pid " +
+ Binder.getCallingPid() + " / uid "
+ Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING");
- return AudioManager.ERROR;
+ return null;
}
+ // policy registered?
+ final AudioPolicyProxy app = mAudioPolicies.get(pcb.asBinder());
+ if (app == null) {
+ Slog.w(TAG, errorMsg + " for pid " +
+ + Binder.getCallingPid() + " / uid "
+ + Binder.getCallingUid() + ", unregistered policy");
+ return null;
+ }
+ return app;
+ }
+ public int addMixForPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb) {
+ if (DEBUG_AP) { Log.d(TAG, "addMixForPolicy for " + pcb.asBinder()
+ + " with config:" + policyConfig); }
synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot add AudioMix in audio policy");
+ if (app == null){
+ return AudioManager.ERROR;
+ }
+ app.addMixes(policyConfig.getMixes());
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ public int removeMixForPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb) {
+ if (DEBUG_AP) { Log.d(TAG, "removeMixForPolicy for " + pcb.asBinder()
+ + " with config:" + policyConfig); }
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot add AudioMix in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+ app.removeMixes(policyConfig.getMixes());
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
+ if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
+ + " policy " + pcb.asBinder());
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot change audio policy focus properties");
+ if (app == null){
+ return AudioManager.ERROR;
+ }
if (!mAudioPolicies.containsKey(pcb.asBinder())) {
Slog.e(TAG, "Cannot change audio policy focus properties, unregistered policy");
return AudioManager.ERROR;
}
- final AudioPolicyProxy app = mAudioPolicies.get(pcb.asBinder());
if (duckingBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) {
// is there already one policy managing ducking?
for (AudioPolicyProxy policy : mAudioPolicies.values()) {
@@ -7290,6 +7339,24 @@
Binder.restoreCallingIdentity(identity);
}
+ void addMixes(@NonNull ArrayList<AudioMix> mixes) {
+ // TODO optimize to not have to unregister the mixes already in place
+ synchronized (mMixes) {
+ AudioSystem.registerPolicyMixes(mMixes, false);
+ this.add(mixes);
+ AudioSystem.registerPolicyMixes(mMixes, true);
+ }
+ }
+
+ void removeMixes(@NonNull ArrayList<AudioMix> mixes) {
+ // TODO optimize to not have to unregister the mixes already in place
+ synchronized (mMixes) {
+ AudioSystem.registerPolicyMixes(mMixes, false);
+ this.remove(mixes);
+ AudioSystem.registerPolicyMixes(mMixes, true);
+ }
+ }
+
void connectMixes() {
final long identity = Binder.clearCallingIdentity();
AudioSystem.registerPolicyMixes(mMixes, true);
@@ -7407,7 +7474,8 @@
//======================
// misc
//======================
- private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
+ private final HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
new HashMap<IBinder, AudioPolicyProxy>();
- private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies
+ @GuardedBy("mAudioPolicies")
+ private int mAudioPolicyCounter = 0;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bb46d5e..c9bdcf1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -57,6 +57,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
+import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
@@ -105,6 +106,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -113,6 +115,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
@@ -131,6 +134,24 @@
// the device idle whitelist during service launch and VPN bootstrap.
private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000;
+ // Settings for how much of the address space should be routed so that Vpn considers
+ // "most" of the address space is routed. This is used to determine whether this Vpn
+ // should be marked with the INTERNET capability.
+ private static final long MOST_IPV4_ADDRESSES_COUNT;
+ private static final BigInteger MOST_IPV6_ADDRESSES_COUNT;
+ static {
+ // 85% of the address space must be routed for Vpn to consider this VPN to provide
+ // INTERNET access.
+ final int howManyPercentIsMost = 85;
+
+ final long twoPower32 = 1L << 32;
+ MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100;
+ final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128);
+ MOST_IPV6_ADDRESSES_COUNT = twoPower128
+ .multiply(BigInteger.valueOf(howManyPercentIsMost))
+ .divide(BigInteger.valueOf(100));
+ }
+
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -830,10 +851,39 @@
return lp;
}
+ /**
+ * Analyzes the passed LinkedProperties to figure out whether it routes to most of the IP space.
+ *
+ * This returns true if the passed LinkedProperties contains routes to either most of the IPv4
+ * space or to most of the IPv6 address space, where "most" is defined by the value of the
+ * MOST_IPV{4,6}_ADDRESSES_COUNT constants : if more than this number of addresses are matched
+ * by any of the routes, then it's decided that most of the space is routed.
+ * @hide
+ */
+ @VisibleForTesting
+ static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+ final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
+ TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
+ TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
+ for (final RouteInfo route : lp.getAllRoutes()) {
+ IpPrefix destination = route.getDestination();
+ if (destination.isIPv4()) {
+ ipv4Prefixes.add(destination);
+ } else {
+ ipv6Prefixes.add(destination);
+ }
+ }
+ if (NetworkUtils.routedIPv4AddressCount(ipv4Prefixes) > MOST_IPV4_ADDRESSES_COUNT) {
+ return true;
+ }
+ return NetworkUtils.routedIPv6AddressCount(ipv6Prefixes)
+ .compareTo(MOST_IPV6_ADDRESSES_COUNT) >= 0;
+ }
+
private void agentConnect() {
LinkProperties lp = makeLinkProperties();
- if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
+ if (providesRoutesToMostDestinations(lp)) {
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
} else {
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 524de91..df60c66 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -70,6 +70,8 @@
import java.util.ArrayList;
import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -225,15 +227,24 @@
* @return List of recent {@link BrightnessChangeEvent}s
*/
public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
- // TODO include apps from any managed profiles in the brightness information.
BrightnessChangeEvent[] events;
synchronized (mEventsLock) {
events = mEvents.toArray();
}
+ int[] profiles = mInjector.getProfileIds(mUserManager, userId);
+ Map<Integer, Boolean> toRedact = new HashMap<>();
+ for (int i = 0; i < profiles.length; ++i) {
+ int profileId = profiles[i];
+ // Include slider interactions when a managed profile app is in the
+ // foreground but always redact the package name.
+ boolean redact = (!includePackage) || profileId != userId;
+ toRedact.put(profiles[i], redact);
+ }
ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
for (int i = 0; i < events.length; ++i) {
- if (events[i].userId == userId) {
- if (includePackage) {
+ Boolean redact = toRedact.get(events[i].userId);
+ if (redact != null) {
+ if (!redact) {
out.add(events[i]);
} else {
BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
@@ -817,6 +828,14 @@
return userManager.getUserHandle(userSerialNumber);
}
+ public int[] getProfileIds(UserManager userManager, int userId) {
+ if (userManager != null) {
+ return userManager.getProfileIds(userId, false);
+ } else {
+ return new int[]{userId};
+ }
+ }
+
public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
return ActivityManager.getService().getFocusedStackInfo();
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5267f54..729ac0c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -459,7 +459,7 @@
private final PendingIntent mWakeupIntent;
private final PendingIntent mTimeoutIntent;
- private final IAppOpsService mAppOpsService;
+ private final AppOpsManager mAppOps;
private final IBatteryStats mBatteryStats;
// Current list of underlying location clients.
@@ -782,8 +782,7 @@
mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// App ops service to keep track of who is accessing the GPS
- mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
- Context.APP_OPS_SERVICE));
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
// Battery statistics service to be notified when GPS turns on or off
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -1490,26 +1489,16 @@
if (newChains != null) {
for (int i = 0; i < newChains.size(); ++i) {
final WorkChain newChain = newChains.get(i);
- try {
- mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, newChain.getAttributionUid(),
- newChain.getAttributionTag());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
+ newChain.getAttributionTag());
}
}
if (goneChains != null) {
for (int i = 0; i < goneChains.size(); i++) {
final WorkChain goneChain = goneChains.get(i);
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
- goneChain.getAttributionTag());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
+ goneChain.getAttributionTag());
}
}
@@ -1525,24 +1514,15 @@
// Update sources that were not previously tracked.
if (newWork != null) {
for (int i = 0; i < newWork.size(); i++) {
- try {
- mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, newWork.get(i), newWork.getName(i));
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.startOpNoThrow(AppOpsManager.OP_GPS,
+ newWork.get(i), newWork.getName(i));
}
}
// Update sources that are no longer tracked.
if (goneWork != null) {
for (int i = 0; i < goneWork.size(); i++) {
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.finishOp(AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 752ab8f..a572cdf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1983,6 +1983,14 @@
}
@Override
+ public void initRecoveryServiceWithSigFile(@NonNull String rootCertificateAlias,
+ @NonNull byte[] recoveryServiceCertFile, @NonNull byte[] recoveryServiceSigFile)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(rootCertificateAlias,
+ recoveryServiceCertFile, recoveryServiceSigFile);
+ }
+
+ @Override
public KeyChainSnapshot getKeyChainSnapshot() throws RemoteException {
return mRecoverableKeyStoreManager.getKeyChainSnapshot();
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 1e0703a..20f3403 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -20,6 +20,7 @@
import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED;
@@ -44,6 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
+import com.android.server.locksettings.recoverablekeystore.certificate.SigXml;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
@@ -160,6 +162,9 @@
}
}
+ /**
+ * @deprecated Use {@link #initRecoveryServiceWithSigFile(String, byte[], byte[])} instead.
+ */
public void initRecoveryService(
@NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile)
throws RemoteException {
@@ -167,8 +172,6 @@
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- // TODO: Check the public-key signature on the whole file before parsing it
-
CertXml certXml;
try {
certXml = CertXml.parse(recoveryServiceCertFile);
@@ -204,7 +207,7 @@
} catch (CertValidationException e) {
Log.e(TAG, "Invalid endpoint cert", e);
throw new ServiceSpecificException(
- ERROR_BAD_CERTIFICATE_FORMAT, "Failed to validate certificate.");
+ ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
}
try {
Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
@@ -219,6 +222,50 @@
}
}
+ /**
+ * Initializes the recovery service with the two files {@code recoveryServiceCertFile} and
+ * {@code recoveryServiceSigFile}.
+ *
+ * @param rootCertificateAlias the alias for the root certificate that is used for validating
+ * the recovery service certificates.
+ * @param recoveryServiceCertFile the content of the XML file containing a list of certificates
+ * for the recovery service.
+ * @param recoveryServiceSigFile the content of the XML file containing the public-key signature
+ * over the entire content of {@code recoveryServiceCertFile}.
+ */
+ public void initRecoveryServiceWithSigFile(
+ @NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile,
+ @NonNull byte[] recoveryServiceSigFile)
+ throws RemoteException {
+ if (recoveryServiceCertFile == null || recoveryServiceSigFile == null) {
+ Log.d(TAG, "The given cert or sig file is null");
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "The given cert or sig file is null.");
+ }
+
+ SigXml sigXml;
+ try {
+ sigXml = SigXml.parse(recoveryServiceSigFile);
+ } catch (CertParsingException e) {
+ Log.d(TAG, "Failed to parse the sig file: " + HexDump.toHexString(
+ recoveryServiceSigFile));
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
+ }
+
+ try {
+ sigXml.verifyFileSignature(TrustedRootCert.TRUSTED_ROOT_CERT, recoveryServiceCertFile);
+ } catch (CertValidationException e) {
+ Log.d(TAG, "The signature over the cert file is invalid."
+ + " Cert: " + HexDump.toHexString(recoveryServiceCertFile)
+ + " Sig: " + HexDump.toHexString(recoveryServiceSigFile));
+ throw new ServiceSpecificException(
+ ERROR_INVALID_CERTIFICATE, "The signature over the cert file is invalid.");
+ }
+
+ initRecoveryService(rootCertificateAlias, recoveryServiceCertFile);
+ }
+
private PublicKey parseEcPublicKey(@NonNull byte[] bytes) throws ServiceSpecificException {
try {
KeyFactory kf = KeyFactory.getInstance("EC");
@@ -392,7 +439,7 @@
// verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned
// by the original recovery service.
if (!publicKeysMatch(publicKey, vaultParams)) {
- throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+ throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE,
"The public keys given in verifierPublicKey and vaultParams do not match.");
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b110e88..84c889c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -92,6 +92,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.NoSuchElementException;
/**
* System implementation of MediaSessionManager
@@ -139,6 +140,8 @@
// TODO(jaewan): Make it priority list for handling volume/media key.
private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>();
+ private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>();
+
public MediaSessionService(Context context) {
super(context);
mSessionManagerImpl = new SessionManagerImpl();
@@ -521,6 +524,7 @@
synchronized (mLock) {
// List to keep the session services that need be removed because they don't exist
// in the 'services' above.
+ boolean notifySessionTokensUpdated = false;
Set<SessionToken2> sessionTokensToRemove = new HashSet<>();
for (SessionToken2 token : mSessionRecords.keySet()) {
if (token.getType() != TYPE_SESSION) {
@@ -553,11 +557,17 @@
// sessionTokensToRemove.
if (!sessionTokensToRemove.remove(token)) {
// New session service is found.
- mSessionRecords.put(token, null);
+ notifySessionTokensUpdated |= addSessionRecordLocked(token);
}
}
for (SessionToken2 token : sessionTokensToRemove) {
mSessionRecords.remove(token);
+ notifySessionTokensUpdated |= removeSessionRecordLocked(token);
+ }
+
+ if (notifySessionTokensUpdated) {
+ // TODO(jaewan): Pass proper user id to postSessionTokensUpdated(...)
+ postSessionTokensUpdated(UserHandle.USER_ALL);
}
}
if (DEBUG) {
@@ -780,10 +790,14 @@
void destroySession2Internal(SessionToken2 token) {
synchronized (mLock) {
+ boolean notifySessionTokensUpdated = false;
if (token.getType() == SessionToken2.TYPE_SESSION) {
- mSessionRecords.remove(token);
+ notifySessionTokensUpdated |= removeSessionRecordLocked(token);
} else {
- mSessionRecords.put(token, null);
+ notifySessionTokensUpdated |= addSessionRecordLocked(token);
+ }
+ if (notifySessionTokensUpdated) {
+ postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
}
}
}
@@ -1517,8 +1531,11 @@
return false;
}
Context context = getContext();
- mSessionRecords.put(token, new MediaController2(context, token,
- context.getMainExecutor(), new ControllerCallback(token)));
+ controller = new MediaController2(context, token, context.getMainExecutor(),
+ new ControllerCallback(token));
+ if (addSessionRecordLocked(token, controller)) {
+ postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
+ }
return true;
}
}
@@ -1571,16 +1588,37 @@
}
// TODO(jaewan): Protect this API with permission
+ // TODO(jaewan): "userId != calling user" needs extra protection
@Override
public void addSessionTokensListener(ISessionTokensListener listener, int userId,
String packageName) {
- // TODO(jaewan): Implement.
+ synchronized (mLock) {
+ final SessionTokensListenerRecord record =
+ new SessionTokensListenerRecord(listener, userId);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ }
+ mSessionTokensListeners.add(record);
+ }
}
// TODO(jaewan): Protect this API with permission
@Override
public void removeSessionTokensListener(ISessionTokensListener listener) {
- // TODO(jaewan): Implement
+ synchronized (mLock) {
+ IBinder listenerBinder = listener.asBinder();
+ for (SessionTokensListenerRecord record : mSessionTokensListeners) {
+ if (listenerBinder.equals(record.mListener.asBinder())) {
+ try {
+ listenerBinder.unlinkToDeath(record, 0);
+ } catch (NoSuchElementException e) {
+ }
+ mSessionTokensListeners.remove(record);
+ break;
+ }
+ }
+ }
}
private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
@@ -1944,6 +1982,7 @@
final class MessageHandler extends Handler {
private static final int MSG_SESSIONS_CHANGED = 1;
private static final int MSG_VOLUME_INITIAL_DOWN = 2;
+ private static final int MSG_SESSIONS_TOKENS_CHANGED = 3;
private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
@Override
@@ -1962,6 +2001,9 @@
}
}
break;
+ case MSG_SESSIONS_TOKENS_CHANGED:
+ pushSessionTokensChanged((int) msg.obj);
+ break;
}
}
@@ -1990,4 +2032,74 @@
destroySession2Internal(mToken);
}
};
+
+ private final class SessionTokensListenerRecord implements IBinder.DeathRecipient {
+ private final ISessionTokensListener mListener;
+ private final int mUserId;
+
+ public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) {
+ mListener = listener;
+ mUserId = userId; // TODO should userId be mapped through mFullUserIds?
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mSessionTokensListeners.remove(this);
+ }
+ }
+ }
+
+ private void postSessionTokensUpdated(int userId) {
+ mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget();
+ }
+
+ private void pushSessionTokensChanged(int userId) {
+ synchronized (mLock) {
+ List<Bundle> tokens = new ArrayList<>();
+ for (SessionToken2 token : mSessionRecords.keySet()) {
+ // TODO(jaewan): Remove the check for UserHandle.USER_ALL (shouldn't happen).
+ // This happens when called form buildMediaSessionService2List(...).
+ if (UserHandle.getUserId(token.getUid()) == userId
+ || UserHandle.USER_ALL == userId) {
+ tokens.add(token.toBundle());
+ }
+ }
+
+ for (SessionTokensListenerRecord record : mSessionTokensListeners) {
+ // TODO should userId be mapped through mFullUserIds?
+ if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) {
+ try {
+ record.mListener.onSessionTokensChanged(tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session tokens changed", e);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean addSessionRecordLocked(SessionToken2 token) {
+ return addSessionRecordLocked(token, null);
+ }
+
+ private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) {
+ if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) {
+ // The key/value pair already exists, no need to update.
+ return false;
+ }
+
+ mSessionRecords.put(token, controller);
+ return true;
+ }
+
+ private boolean removeSessionRecordLocked(SessionToken2 token) {
+ if (!mSessionRecords.containsKey(token)) {
+ // The key is already removed, no need to remove.
+ return false;
+ }
+
+ mSessionRecords.remove(token);
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
index 6921ccd..f38b353 100644
--- a/services/core/java/com/android/server/media/MediaUpdateService.java
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -16,27 +16,21 @@
package com.android.server.media;
-import android.content.Context;
-import android.content.Intent;
-import android.media.IMediaResourceMonitor;
-import android.os.Binder;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.Slog;
-import com.android.server.SystemService;
-
import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.media.IMediaExtractorUpdateService;
import android.os.IBinder;
+import android.os.Handler;
import android.os.PatternMatcher;
import android.os.ServiceManager;
-import android.media.IMediaExtractorUpdateService;
-
-import java.lang.Exception;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
/** This class provides a system service that manages media framework updates. */
public class MediaUpdateService extends SystemService {
@@ -46,9 +40,11 @@
private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
private IMediaExtractorUpdateService mMediaExtractorUpdateService;
+ final Handler mHandler;
public MediaUpdateService(Context context) {
super(context);
+ mHandler = new Handler();
}
@Override
@@ -77,7 +73,12 @@
}
if (binder != null) {
mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
- packageStateChanged();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ packageStateChanged();
+ }
+ });
} else {
Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index debfbc0..efc948b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -25,6 +25,8 @@
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
@@ -65,6 +67,10 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
+import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
+import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1477,7 +1483,8 @@
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
- publishBinderService(Context.NOTIFICATION_SERVICE, mService);
+ publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
+ DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
@@ -2916,6 +2923,8 @@
dumpJson(pw, filter);
} else if (filter.proto) {
dumpProto(fd, filter);
+ } else if (filter.criticalPriority) {
+ dumpNotificationRecords(pw, filter);
} else {
dumpImpl(pw, filter);
}
@@ -3541,6 +3550,22 @@
proto.flush();
}
+ private void dumpNotificationRecords(PrintWriter pw, @NonNull DumpFilter filter) {
+ synchronized (mNotificationLock) {
+ int N;
+ N = mNotificationList.size();
+ if (N > 0) {
+ pw.println(" Notification List:");
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord nr = mNotificationList.get(i);
+ if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ nr.dump(pw, " ", getContext(), filter.redact);
+ }
+ pw.println(" ");
+ }
+ }
+ }
+
void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
pw.print("Current Notification Manager state");
if (filter.filtered) {
@@ -3565,17 +3590,11 @@
synchronized (mNotificationLock) {
if (!zenOnly) {
- N = mNotificationList.size();
- if (N > 0) {
- pw.println(" Notification List:");
- for (int i=0; i<N; i++) {
- final NotificationRecord nr = mNotificationList.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
- nr.dump(pw, " ", getContext(), filter.redact);
- }
- pw.println(" ");
+ // Priority filters are only set when called via bugreport. If set
+ // skip sections that are part of the critical section.
+ if (!filter.normalPriority) {
+ dumpNotificationRecords(pw, filter);
}
-
if (!filter.filtered) {
N = mLights.size();
if (N > 0) {
@@ -6259,6 +6278,8 @@
public boolean stats;
public boolean redact = true;
public boolean proto = false;
+ public boolean criticalPriority = false;
+ public boolean normalPriority = false;
@NonNull
public static DumpFilter parseFromArguments(String[] args) {
@@ -6290,6 +6311,21 @@
} else {
filter.since = 0;
}
+ } else if (PRIORITY_ARG.equals(a)) {
+ // Bugreport will call the service twice with priority arguments, first to dump
+ // critical sections and then non critical ones. Set approriate filters
+ // to generate the desired data.
+ if (ai < args.length - 1) {
+ ai++;
+ switch (args[ai]) {
+ case PRIORITY_ARG_CRITICAL:
+ filter.criticalPriority = true;
+ break;
+ case PRIORITY_ARG_NORMAL:
+ filter.normalPriority = true;
+ break;
+ }
+ }
}
}
return filter;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0a87097..1a19698 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -107,7 +107,7 @@
@VisibleForTesting protected int mZenMode;
private int mUser = UserHandle.USER_SYSTEM;
@VisibleForTesting protected ZenModeConfig mConfig;
- private AudioManagerInternal mAudioManager;
+ @VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
private long mSuppressedEffects;
@@ -886,7 +886,8 @@
exceptionPackages);
}
- private void applyZenToRingerMode() {
+ @VisibleForTesting
+ protected void applyZenToRingerMode() {
if (mAudioManager == null) return;
// force the ringer mode into compliance
final int ringerModeInternal = mAudioManager.getRingerModeInternal();
@@ -901,15 +902,8 @@
break;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
if (ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) {
- if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
- setPreviousRingerModeSetting(ringerModeInternal);
- newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
- }
- } else {
- if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
- newRingerModeInternal = getPreviousRingerModeSetting();
- setPreviousRingerModeSetting(null);
- }
+ setPreviousRingerModeSetting(ringerModeInternal);
+ newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
}
break;
case Global.ZEN_MODE_OFF:
@@ -1003,7 +997,8 @@
}
}
- private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
+ @VisibleForTesting
+ protected final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
@Override
public String toString() {
return TAG;
@@ -1040,9 +1035,14 @@
}
break;
}
+
if (newZen != -1) {
setManualZenMode(newZen, null, "ringerModeInternal", null,
false /*setRingerMode*/);
+ } else if (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) {
+ // in priority only with ringer not muted, save ringer mode changes
+ setPreviousRingerModeSetting(ringerModeNew);
}
if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index 98f421e..16b4368 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import android.annotation.AnyThread;
+import android.annotation.WorkerThread;
import android.app.IInstantAppResolver;
import android.app.InstantAppResolverService;
import android.content.ComponentName;
@@ -37,6 +39,7 @@
import android.util.TimedRemoteCaller;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +71,7 @@
private static final int STATE_IDLE = 0; // no bind operation is ongoing
private static final int STATE_BINDING = 1; // someone is binding and waiting
private static final int STATE_PENDING = 2; // a bind is pending, but the caller is not waiting
+ private final Handler mBgHandler;
@GuardedBy("mLock")
private int mBindState = STATE_IDLE;
@@ -78,6 +82,7 @@
Context context, ComponentName componentName, String action) {
mContext = context;
mIntent = new Intent(action).setComponent(componentName);
+ mBgHandler = BackgroundThread.getHandler();
}
public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(Intent sanitizedIntent,
@@ -131,6 +136,7 @@
}
}
+ @WorkerThread
private IInstantAppResolver getRemoteInstanceLazy(String token)
throws ConnectionException, TimeoutException, InterruptedException {
long binderToken = Binder.clearCallingIdentity();
@@ -157,6 +163,7 @@
}
}
+ @WorkerThread
private IInstantAppResolver bind(String token)
throws ConnectionException, TimeoutException, InterruptedException {
boolean doUnbind = false;
@@ -241,6 +248,19 @@
}
}
+ @AnyThread
+ void optimisticBind() {
+ mBgHandler.post(() -> {
+ try {
+ if (bind("Optimistic Bind") != null && DEBUG_INSTANT) {
+ Slog.i(TAG, "Optimistic bind succeeded.");
+ }
+ } catch (ConnectionException | TimeoutException | InterruptedException e) {
+ Slog.e(TAG, "Optimistic bind failed.", e);
+ }
+ });
+ }
+
@Override
public void binderDied() {
if (DEBUG_INSTANT) {
@@ -249,6 +269,7 @@
synchronized (mLock) {
handleBinderDiedLocked();
}
+ optimisticBind();
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9c69281..ee32618 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1613,6 +1613,8 @@
writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
+ writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
+
// Persist app icon if changed since last written
File appIconFile = buildAppIconFile(sessionId, sessionsDir);
if (params.appIcon == null && appIconFile.exists()) {
@@ -1632,8 +1634,6 @@
params.appIconLastModified = appIconFile.lastModified();
}
-
- writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
}
out.endTag(null, TAG_SESSION);
@@ -1711,9 +1711,10 @@
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
- params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
+ params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+
final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
if (appIconFile.exists()) {
params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5cf7903..98fb18a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20873,6 +20873,16 @@
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.systemReady();
+
+ if (mInstantAppResolverConnection != null) {
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mInstantAppResolverConnection.optimisticBind();
+ mContext.unregisterReceiver(this);
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+ }
}
public void waitForAppDataPrepared() {
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index cedf476..0fecb63 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -230,7 +230,7 @@
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
- getPackageInfo().saveToXml(out, forBackup);
+ getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
for (int i = 0; i < size; i++) {
final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c11c099..92e261a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1348,7 +1348,7 @@
ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
- getPackageInfo().saveToXml(out, forBackup);
+ getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
for (int j = 0; j < size; j++) {
saveShortcut(out, mShortcuts.valueAt(j), forBackup,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index f5edae0..44dd924 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -50,6 +50,7 @@
private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
private static final String ATTR_BACKUP_SOURCE_VERSION = "bk_src_version";
private static final String ATTR_BACKUP_ALLOWED = "allow-backup";
+ private static final String ATTR_BACKUP_ALLOWED_INITIALIZED = "allow-backup-initialized";
private static final String ATTR_BACKUP_SOURCE_BACKUP_ALLOWED = "bk_src_backup-allowed";
private static final String ATTR_SHADOW = "shadow";
@@ -190,7 +191,11 @@
mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
}
- public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException {
+ public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
+ throws IOException {
+ if (forBackup && !mBackupAllowedInitialized) {
+ s.wtf("Backup happened before mBackupAllowed is initialized.");
+ }
out.startTag(null, TAG_ROOT);
@@ -199,6 +204,10 @@
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED, mBackupAllowed);
+ // We don't need to save this field (we don't even read it back), but it'll show up
+ // in the dumpsys in the backup / restore payload.
+ ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED_INITIALIZED, mBackupAllowedInitialized);
+
ShortcutService.writeAttr(out, ATTR_BACKUP_SOURCE_VERSION, mBackupSourceVersionCode);
ShortcutService.writeAttr(out,
ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, mBackupSourceBackupAllowed);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ca6f53a..70fb616 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3546,9 +3546,11 @@
// Update the signatures for all packages.
user.forAllPackageItems(spi -> spi.refreshPackageSignatureAndSave());
+ // Rescan all apps; this will also update the version codes and "allow-backup".
+ user.forAllPackages(pkg -> pkg.rescanPackageIfNeeded(
+ /*isNewApp=*/ false, /*forceRescan=*/ true));
+
// Set the version code for the launchers.
- // We shouldn't do this for publisher packages, because we don't want to update the
- // version code without rescanning the manifest.
user.forAllLaunchers(launcher -> launcher.ensurePackageInfo());
// Save to the filesystem.
@@ -3567,7 +3569,9 @@
Slog.w(TAG, "Backup failed.", e);
return null;
}
- return os.toByteArray();
+ byte[] payload = os.toByteArray();
+ mShortcutDumpFiles.save("backup-1-payload.txt", payload);
+ return payload;
}
}
@@ -3847,6 +3851,8 @@
pw.print(next);
pw.print("] ");
pw.print(formatTime(next));
+ pw.println();
+ pw.println();
pw.print(" Config:");
pw.print(" Max icon dim: ");
@@ -4242,7 +4248,6 @@
}
// Injection point.
- @VisibleForTesting
String injectBuildFingerprint() {
return Build.FINGERPRINT;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index c044c1c..505e4ee 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -23,7 +23,6 @@
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -62,6 +61,7 @@
// Suffix "2" was added to force rescan all packages after the next OTA.
private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2";
private static final String ATTR_LAST_APP_SCAN_OS_FINGERPRINT = "last-app-scan-fp";
+ private static final String ATTR_RESTORE_SOURCE_FINGERPRINT = "restore-from-fp";
private static final String KEY_USER_ID = "userId";
private static final String KEY_LAUNCHERS = "launchers";
private static final String KEY_PACKAGES = "packages";
@@ -128,6 +128,7 @@
private long mLastAppScanTime;
private String mLastAppScanOsFingerprint;
+ private String mRestoreFromOsFingerprint;
public ShortcutUser(ShortcutService service, int userId) {
mService = service;
@@ -340,8 +341,13 @@
mLastAppScanTime);
ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_OS_FINGERPRINT,
mLastAppScanOsFingerprint);
+ ShortcutService.writeAttr(out, ATTR_RESTORE_SOURCE_FINGERPRINT,
+ mRestoreFromOsFingerprint);
ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher);
+ } else {
+ ShortcutService.writeAttr(out, ATTR_RESTORE_SOURCE_FINGERPRINT,
+ mService.injectBuildFingerprint());
}
// Can't use forEachPackageItem due to the checked exceptions.
@@ -387,6 +393,8 @@
ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0;
ret.mLastAppScanOsFingerprint = ShortcutService.parseStringAttribute(parser,
ATTR_LAST_APP_SCAN_OS_FINGERPRINT);
+ ret.mRestoreFromOsFingerprint = ShortcutService.parseStringAttribute(parser,
+ ATTR_RESTORE_SOURCE_FINGERPRINT);
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -524,6 +532,8 @@
restored.mLaunchers.clear();
restored.mPackages.clear();
+ mRestoreFromOsFingerprint = restored.mRestoreFromOsFingerprint;
+
Slog.i(TAG, "Restored: L=" + restoredLaunchers[0]
+ " P=" + restoredPackages[0]
+ " S=" + restoredShortcuts[0]);
@@ -539,14 +549,21 @@
pw.print(" Last app scan: [");
pw.print(mLastAppScanTime);
pw.print("] ");
- pw.print(ShortcutService.formatTime(mLastAppScanTime));
- pw.print(" Last app scan FP: ");
- pw.print(mLastAppScanOsFingerprint);
- pw.println();
+ pw.println(ShortcutService.formatTime(mLastAppScanTime));
prefix += prefix + " ";
pw.print(prefix);
+ pw.print("Last app scan FP: ");
+ pw.println(mLastAppScanOsFingerprint);
+
+ pw.print(prefix);
+ pw.print("Restore from FP: ");
+ pw.print(mRestoreFromOsFingerprint);
+ pw.println();
+
+
+ pw.print(prefix);
pw.print("Cached launcher: ");
pw.print(mCachedLauncher);
pw.println();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index de7e21a..2c7df6c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2584,9 +2584,6 @@
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
return null;
}
- if (ActivityManager.isLowRamDeviceStatic()) {
- return null;
- }
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7efc9876..eaa17ca 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -485,7 +485,7 @@
boolean mSafeMode;
private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
WindowState mStatusBar = null;
- int mStatusBarHeight;
+ private final int[] mStatusBarHeightForRotation = new int[4];
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
@@ -2768,8 +2768,12 @@
Context uiContext = getSystemUiContext();
final Resources res = uiContext.getResources();
- mStatusBarHeight =
- res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mStatusBarHeightForRotation[mPortraitRotation] =
+ mStatusBarHeightForRotation[mUpsideDownRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ mStatusBarHeightForRotation[mLandscapeRotation] =
+ mStatusBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
// Height of the navigation bar when presented horizontally at bottom
mNavigationBarHeightForRotationDefault[mPortraitRotation] =
@@ -2884,11 +2888,11 @@
// of the screen.
// TODO(multi-display): Support status bars on secondary displays.
if (displayId == DEFAULT_DISPLAY) {
- int statusBarHeight = mStatusBarHeight;
+ int statusBarHeight = mStatusBarHeightForRotation[rotation];
if (displayCutout != null) {
// If there is a cutout, it may already have accounted for some part of the status
// bar height.
- statusBarHeight = Math.max(0, mStatusBarHeight - displayCutout.getSafeInsetTop());
+ statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
}
return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId,
displayCutout) - statusBarHeight;
@@ -4649,7 +4653,8 @@
displayFrames.mDisplayCutout);
// For layout, the status bar is always at the top with our fixed height.
- displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
+ displayFrames.mStable.top = displayFrames.mUnrestricted.top
+ + mStatusBarHeightForRotation[displayFrames.mRotation];
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
@@ -5642,9 +5647,7 @@
final int fl = PolicyControl.getWindowFlags(null,
mTopFullscreenOpaqueWindowState.getAttrs());
if (localLOGV) {
- Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
- + " shown position: "
- + mTopFullscreenOpaqueWindowState.getShownPositionLw());
+ Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
}
@@ -6938,7 +6941,7 @@
// Navigation bar and status bar.
getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
- outInsets.top = Math.max(outInsets.top, mStatusBarHeight);
+ outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
}
@Override
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index bf0c3da..a07f5eb 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -232,14 +232,6 @@
public Rect getFrameLw();
/**
- * Retrieve the current position of the window that is actually shown.
- * Must be called with the window manager lock held.
- *
- * @return Point The point holding the shown window position.
- */
- public Point getShownPositionLw();
-
- /**
* Retrieve the frame of the display that this window was last
* laid out in. Must be called with the
* window manager lock held.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3072f21..b729b6a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -91,7 +91,7 @@
private final Context mContext;
private final IBatteryStats mBatteryStats;
- private final IAppOpsService mAppOps;
+ private final AppOpsManager mAppOps;
private final SuspendBlocker mSuspendBlocker;
private final WindowManagerPolicy mPolicy;
private final ActivityManagerInternal mActivityManagerInternal;
@@ -134,11 +134,10 @@
private boolean mUserActivityPending;
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
- IAppOpsService appOps, SuspendBlocker suspendBlocker,
- WindowManagerPolicy policy) {
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
mContext = context;
mBatteryStats = batteryStats;
- mAppOps = appOps;
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -194,8 +193,7 @@
mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
monitorType, unimportantForLogging);
// XXX need to deal with disabled operations.
- mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
- AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -295,8 +293,7 @@
} else {
mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
historyTag, monitorType);
- mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
- AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -539,12 +536,11 @@
try {
mBatteryStats.noteWakeUp(reason, reasonUid);
if (opPackageName != null) {
- mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
+ mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
}
} catch (RemoteException ex) {
// Ignore
}
-
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d67acc4..f77b0ee 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -759,8 +759,7 @@
// with the animations and other critical functions of the power manager.
mBatteryStats = BatteryStatsService.getService();
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
- mPolicy);
+ createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 32f38b7..a9a1456 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -347,7 +347,7 @@
Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
.putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
diff --git a/services/core/java/com/android/server/utils/PriorityDump.java b/services/core/java/com/android/server/utils/PriorityDump.java
index fb92c2b..d9fb9c0 100644
--- a/services/core/java/com/android/server/utils/PriorityDump.java
+++ b/services/core/java/com/android/server/utils/PriorityDump.java
@@ -94,11 +94,7 @@
* <p>To run the unit tests:
* <pre><code>
*
- mmm -j32 frameworks/base/services/tests/servicestests/ && \
- adb install -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
- adb shell am instrument -e class "com.android.server.utils.PriorityDumpTest" \
- -w "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
-
+ atest FrameworksServicesTests:PriorityDumpTest
* </code></pre>
*
*
@@ -108,6 +104,9 @@
public static final String PRIORITY_ARG = "--dump-priority";
public static final String PROTO_ARG = "--proto";
+ public static final String PRIORITY_ARG_CRITICAL = "CRITICAL";
+ public static final String PRIORITY_ARG_HIGH = "HIGH";
+ public static final String PRIORITY_ARG_NORMAL = "NORMAL";
private PriorityDump() {
throw new UnsupportedOperationException();
@@ -191,17 +190,19 @@
*/
private static @PriorityType int getPriorityType(String arg) {
switch (arg) {
- case "CRITICAL": {
+ case PRIORITY_ARG_CRITICAL: {
return PRIORITY_TYPE_CRITICAL;
}
- case "HIGH": {
+ case PRIORITY_ARG_HIGH: {
return PRIORITY_TYPE_HIGH;
}
- case "NORMAL": {
+ case PRIORITY_ARG_NORMAL: {
return PRIORITY_TYPE_NORMAL;
}
+ default: {
+ return PRIORITY_TYPE_INVALID;
+ }
}
- return PRIORITY_TYPE_INVALID;
}
/**
@@ -238,7 +239,7 @@
* Dumps all sections.
* <p>
* This method is called when
- * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[], boolean)}
+ * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[])}
* is called without priority arguments. By default, it calls the 3 {@code dumpTYPE}
* methods, so sub-classes just need to implement the priority types they support.
*/
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index db95634..3cd3e8b 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -53,8 +53,7 @@
AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
mAppToken = appToken;
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
- appToken.mService.mAnimator::addAfterPrepareSurfacesRunnable, appToken.mService);
+ mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
mWidth = thumbnailHeader.getWidth();
mHeight = thumbnailHeader.getHeight();
@@ -145,11 +144,6 @@
}
@Override
- public void destroyAfterPendingTransaction(SurfaceControl surface) {
- mAppToken.destroyAfterPendingTransaction(surface);
- }
-
- @Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
t.setLayer(leash, Integer.MAX_VALUE);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c2cc7c9..c04522e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -475,6 +475,20 @@
setClientHidden(!visible);
}
+ if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) {
+ // The token is not closing nor opening, so even if there is an animation set, that
+ // doesn't mean that it goes through the normal app transition cycle so we have
+ // to inform the docked controller about visibility change.
+ // TODO(multi-display): notify docked divider on all displays where visibility was
+ // affected.
+ mService.getDefaultDisplayContentLocked().getDockedDividerController()
+ .notifyAppVisibilityChanged();
+
+ // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
+ // will not be taken.
+ mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+ }
+
// If we are hidden but there is no delay needed we immediately
// apply the Surface transaction so that the ActivityManager
// can have some guarantee on the Surface state following
@@ -492,17 +506,6 @@
}
SurfaceControl.closeTransaction();
}
-
- if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) {
- // The token is not closing nor opening, so even if there is an animation set, that
- // doesn't mean that it goes through the normal app transition cycle so we have
- // to inform the docked controller about visibility change.
- // TODO(multi-display): notify docked divider on all displays where visibility was
- // affected.
- mService.getDefaultDisplayContentLocked().getDockedDividerController()
- .notifyAppVisibilityChanged();
- mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
- }
}
return delayed;
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 4394a99..a180a3a 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -55,11 +55,6 @@
}
@Override
- public void destroyAfterPendingTransaction(SurfaceControl surface) {
- mHost.destroyAfterPendingTransaction(surface);
- }
-
- @Override
public SurfaceControl.Builder makeAnimationLeash() {
return mHost.makeAnimationLeash();
}
@@ -119,7 +114,7 @@
if (!mDimming) {
mDimLayer.destroy();
}
- }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
+ }, mHost.mService);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 19c634a..59bece0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -382,11 +382,6 @@
*/
private int mSurfaceSize;
- /**
- * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
- */
- private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
-
/** Temporary float array to retrieve 3x3 matrix values. */
private final float[] mTmpFloats = new float[9];
@@ -747,7 +742,14 @@
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+ // We use this as our arbitrary surface size for buffer-less parents
+ // that don't impose cropping on their children. It may need to be larger
+ // than the display size because fullscreen windows can be shifted offscreen
+ // due to surfaceInsets. 2 times the largest display dimension feels like an
+ // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
+ // layers the ability to match their parent sizes and be able to skip
+ // such arbitrary size settings.
+ mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
.setSize(mSurfaceSize, mSurfaceSize)
@@ -1954,10 +1956,6 @@
}
}
mService.mAnimator.removeDisplayLocked(mDisplayId);
-
- // The pending transaction won't be applied so we should
- // just clean up any surfaces pending destruction.
- onPendingTransactionApplied();
} finally {
mRemovingDisplay = false;
}
@@ -2600,18 +2598,6 @@
}, false /* traverseTopToBottom */);
}
- void enableSurfaceTrace(FileDescriptor fd) {
- forAllWindows(w -> {
- w.mWinAnimator.enableSurfaceTrace(fd);
- }, true /* traverseTopToBottom */);
- }
-
- void disableSurfaceTrace() {
- forAllWindows(w -> {
- w.mWinAnimator.disableSurfaceTrace();
- }, true /* traverseTopToBottom */);
- }
-
/**
* Starts the Keyguard exit animation on all windows that don't belong to an app token.
*/
@@ -3879,22 +3865,6 @@
}
@Override
- public void destroyAfterPendingTransaction(SurfaceControl surface) {
- mPendingDestroyingSurfaces.add(surface);
- }
-
- /**
- * Destroys any surfaces that have been put into the pending list with
- * {@link #destroyAfterPendingTransaction}.
- */
- void onPendingTransactionApplied() {
- for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
- mPendingDestroyingSurfaces.get(i).destroy();
- }
- mPendingDestroyingSurfaces.clear();
- }
-
- @Override
void prepareSurfaces() {
final ScreenRotationAnimation screenRotationAnimation =
mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
@@ -3908,6 +3878,7 @@
mPendingTransaction.setAlpha(mWindowingLayer,
screenRotationAnimation.getEnterTransformation().getAlpha());
}
+
super.prepareSurfaces();
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 31b5c69..f7344b2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -219,7 +219,7 @@
private void addAnimation(Task task) {
if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
- mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
+ mService);
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
task.commitPendingTransaction();
@@ -256,18 +256,20 @@
void cancelAnimation() {
if (DEBUG) Log.d(TAG, "cancelAnimation()");
- if (mCanceled) {
- // We've already canceled the animation
- return;
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ // We've already canceled the animation
+ return;
+ }
+ mCanceled = true;
+ try {
+ mRunner.onAnimationCanceled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation", e);
+ }
}
- mCanceled = true;
- try {
- mRunner.onAnimationCanceled();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel recents animation", e);
- }
-
// Clean up and return to the previous app
+ // Don't hold the WM lock here as it calls back to AM/RecentsAnimation
mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
}
diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java
deleted file mode 100644
index b214d35..0000000
--- a/services/core/java/com/android/server/wm/RemoteEventTrace.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.DataOutputStream;
-
-import android.os.StrictMode;
-import android.util.Slog;
-import android.os.Debug;
-
-// Counterpart to remote surface trace for events which are not tied to a particular surface.
-class RemoteEventTrace {
- private static final String TAG = "RemoteEventTrace";
-
- // We terminate all our messages with a recognizable marker, to avoid issues
- // with partial reads (which ADB makes impossible to avoid).
- static final byte[] sigil = {(byte)0xfc, (byte)0xfc, (byte)0xfc, (byte)0xfc};
-
- private final WindowManagerService mService;
- private final DataOutputStream mOut;
-
- RemoteEventTrace(WindowManagerService service, FileDescriptor fd) {
- mService = service;
- mOut = new DataOutputStream(new FileOutputStream(fd, false));
- }
-
- void openSurfaceTransaction() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- mOut.writeUTF("OpenTransaction");
- writeSigil();
- } catch (Exception e) {
- logException(e);
- mService.disableSurfaceTrace();
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- void closeSurfaceTransaction() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- mOut.writeUTF("CloseTransaction");
- writeSigil();
- } catch (Exception e) {
- logException(e);
- mService.disableSurfaceTrace();
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- private void writeSigil() throws Exception {
- mOut.write(RemoteEventTrace.sigil, 0, 4);
- }
-
- static void logException(Exception e) {
- Slog.i(TAG, "Exception writing to SurfaceTrace (client vanished?): " + e.toString());
- }
-}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
deleted file mode 100644
index 33e560f..0000000
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.StrictMode;
-import android.util.Slog;
-import android.view.SurfaceControl;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.DataOutputStream;
-
-// A surface control subclass which logs events to a FD in binary format.
-// This can be used in our CTS tests to enable a pattern similar to mocking
-// the surface control.
-//
-// See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
-class RemoteSurfaceTrace extends SurfaceControl {
- static final String TAG = "RemoteSurfaceTrace";
-
- final FileDescriptor mWriteFd;
- final DataOutputStream mOut;
-
- final WindowManagerService mService;
- final WindowState mWindow;
-
- RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped,
- WindowState window) {
- super(wrapped);
-
- mWriteFd = fd;
- mOut = new DataOutputStream(new FileOutputStream(fd, false));
-
- mWindow = window;
- mService = mWindow.mService;
- }
-
- @Override
- public void setAlpha(float alpha) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeFloatEvent("Alpha", alpha);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setAlpha(alpha);
- }
-
- @Override
- public void setLayer(int zorder) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeIntEvent("Layer", zorder);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setLayer(zorder);
- }
-
- @Override
- public void setPosition(float x, float y) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeFloatEvent("Position", x, y);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setPosition(x, y);
- }
-
- @Override
- public void setGeometryAppliesWithResize() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeEvent("GeometryAppliesWithResize");
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setGeometryAppliesWithResize();
- }
-
- @Override
- public void setSize(int w, int h) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeIntEvent("Size", w, h);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setSize(w, h);
- }
-
- @Override
- public void setWindowCrop(Rect crop) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeRectEvent("Crop", crop);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setWindowCrop(crop);
- }
-
- @Override
- public void setFinalCrop(Rect crop) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeRectEvent("FinalCrop", crop);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setFinalCrop(crop);
- }
-
- @Override
- public void setLayerStack(int layerStack) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeIntEvent("LayerStack", layerStack);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setLayerStack(layerStack);
- }
-
- @Override
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.setMatrix(dsdx, dtdx, dsdy, dtdy);
- }
-
- @Override
- public void hide() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeEvent("Hide");
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.hide();
- }
-
- @Override
- public void show() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
- try {
- writeEvent("Show");
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- super.show();
- }
-
- private void writeEvent(String tag) {
- try {
- mOut.writeUTF(tag);
- mOut.writeUTF(mWindow.getWindowTag().toString());
- writeSigil();
- } catch (Exception e) {
- RemoteEventTrace.logException(e);
- mService.disableSurfaceTrace();
- }
- }
-
- private void writeIntEvent(String tag, int... values) {
- try {
- mOut.writeUTF(tag);
- mOut.writeUTF(mWindow.getWindowTag().toString());
- for (int value: values) {
- mOut.writeInt(value);
- }
- writeSigil();
- } catch (Exception e) {
- RemoteEventTrace.logException(e);
- mService.disableSurfaceTrace();
- }
- }
-
- private void writeFloatEvent(String tag, float... values) {
- try {
- mOut.writeUTF(tag);
- mOut.writeUTF(mWindow.getWindowTag().toString());
- for (float value: values) {
- mOut.writeFloat(value);
- }
- writeSigil();
- } catch (Exception e) {
- RemoteEventTrace.logException(e);
- mService.disableSurfaceTrace();
- }
- }
-
- private void writeRectEvent(String tag, Rect value) {
- writeFloatEvent(tag, value.left, value.top, value.right, value.bottom);
- }
-
- private void writeSigil() throws Exception {
- mOut.write(RemoteEventTrace.sigil, 0, 4);
- }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6356a35..f32c275 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -29,9 +29,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -50,9 +48,6 @@
import java.util.List;
import java.util.function.Consumer;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.OP_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -68,8 +63,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -130,13 +123,6 @@
private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
- // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
- // instances will be replaced with an instance that writes a binary representation of all
- // commands to mSurfaceTraceFd.
- boolean mSurfaceTraceEnabled;
- ParcelFileDescriptor mSurfaceTraceFd;
- RemoteEventTrace mRemoteEventTrace;
-
final WallpaperController mWallpaperController;
private final Handler mHandler;
@@ -427,12 +413,7 @@
void updateAppOpsState() {
forAllWindows((w) -> {
- if (w.mAppOp == OP_NONE) {
- return;
- }
- final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(),
- w.getOwningPackage());
- w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
+ w.updateAppOpsState();
}, false /* traverseTopToBottom */);
}
@@ -814,7 +795,6 @@
mService.enableScreenIfNeededLocked();
mService.scheduleAnimationLocked();
- mService.mWindowPlacerLocked.destroyPendingSurfaces();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
"performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
@@ -1014,30 +994,6 @@
}
}
- void enableSurfaceTrace(ParcelFileDescriptor pfd) {
- final FileDescriptor fd = pfd.getFileDescriptor();
- if (mSurfaceTraceEnabled) {
- disableSurfaceTrace();
- }
- mSurfaceTraceEnabled = true;
- mRemoteEventTrace = new RemoteEventTrace(mService, fd);
- mSurfaceTraceFd = pfd;
- for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent dc = mChildren.get(displayNdx);
- dc.enableSurfaceTrace(fd);
- }
- }
-
- void disableSurfaceTrace() {
- mSurfaceTraceEnabled = false;
- mRemoteEventTrace = null;
- mSurfaceTraceFd = null;
- for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent dc = mChildren.get(displayNdx);
- dc.disableSurfaceTrace();
- }
- }
-
void dumpDisplayContents(PrintWriter pw) {
pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
if (mService.mDisplayReady) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 83baee1..76f5396 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -33,7 +33,6 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.function.Consumer;
/**
* A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -60,21 +59,17 @@
/**
* @param animatable The object to animate.
* @param animationFinishedCallback Callback to invoke when an animation has finished running.
- * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
- * surfaces in WM. Can be implemented differently during testing.
*/
SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
- Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
+ WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mAnimationFinishedCallback = animationFinishedCallback;
- mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback,
- addAfterPrepareSurfaces);
+ mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
}
private OnAnimationFinishedCallback getFinishedCallback(
- @Nullable Runnable animationFinishedCallback,
- Consumer<Runnable> addAfterPrepareSurfaces) {
+ @Nullable Runnable animationFinishedCallback) {
return anim -> {
synchronized (mService.mWindowMap) {
final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
@@ -83,30 +78,13 @@
return;
}
- // TODO: This should use pendingTransaction eventually, but right now things
- // happening on the animation finished callback are happening on the global
- // transaction.
- // For now we need to run this after it's guaranteed that the transaction that
- // reparents the surface onto the leash is executed already. Otherwise this may be
- // executed first, leading to surface loss, as the reparent operations wouldn't
- // be in order.
- addAfterPrepareSurfaces.accept(() -> {
- if (anim != mAnimation) {
- // Callback was from another animation - ignore.
- return;
- }
- final Transaction t = new Transaction();
- SurfaceControl.openTransaction();
- try {
- reset(t, true /* destroyLeash */);
- if (animationFinishedCallback != null) {
- animationFinishedCallback.run();
- }
- } finally {
- SurfaceControl.mergeToGlobalTransaction(t);
- SurfaceControl.closeTransaction();
- }
- });
+ if (anim != mAnimation) {
+ return;
+ }
+ reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
+ if (animationFinishedCallback != null) {
+ animationFinishedCallback.run();
+ }
}
};
}
@@ -282,15 +260,19 @@
final SurfaceControl surface = mAnimatable.getSurfaceControl();
final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+ boolean scheduleAnim = false;
+
// If the surface was destroyed, we don't care to reparent it back.
final boolean destroy = mLeash != null && surface != null && parent != null;
if (destroy) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
t.reparent(surface, parent.getHandle());
+ scheduleAnim = true;
}
mService.mAnimationTransferMap.remove(mAnimation);
if (mLeash != null && destroyLeash) {
- mAnimatable.destroyAfterPendingTransaction(mLeash);
+ t.destroy(mLeash);
+ scheduleAnim = true;
}
mLeash = null;
mAnimation = null;
@@ -298,6 +280,11 @@
// Make sure to inform the animatable after the leash was destroyed.
if (destroy) {
mAnimatable.onAnimationLeashDestroyed(t);
+ scheduleAnim = true;
+ }
+
+ if (scheduleAnim) {
+ mService.scheduleAnimationLocked();
}
}
@@ -379,13 +366,6 @@
void onAnimationLeashDestroyed(Transaction t);
/**
- * Destroy a given surface after executing {@link #getPendingTransaction}.
- *
- * @see WindowContainer#destroyAfterPendingTransaction
- */
- void destroyAfterPendingTransaction(SurfaceControl surface);
-
- /**
* @return A new surface to be used for the animation leash, inserted at the correct
* position in the hierarchy.
*/
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ba08fcd2..b5d00a7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -751,8 +751,11 @@
int getStackOutset() {
if (inPinnedWindowingMode()) {
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- return mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
- displayMetrics);
+
+ // We multiply by two to match the client logic for converting view elevation
+ // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
+ return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ displayMetrics) * 2);
}
return 0;
}
@@ -824,6 +827,7 @@
}
updateDisplayInfo(bounds);
+ updateSurfaceBounds();
}
/**
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a7d51f1..2873b6d 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -275,6 +275,8 @@
}
boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
+ int xOffset = 0;
+ int yOffset = 0;
boolean rawChanged = false;
// Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
// match the behavior of most Launchers
@@ -286,11 +288,8 @@
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
}
- boolean changed = wallpaperWin.mXOffset != offset;
- if (changed) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
- wallpaperWin.mXOffset = offset;
- }
+ xOffset = offset;
+
if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
wallpaperWin.mWallpaperX = wpx;
wallpaperWin.mWallpaperXStep = wpxs;
@@ -304,17 +303,16 @@
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
}
- if (wallpaperWin.mYOffset != offset) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
- changed = true;
- wallpaperWin.mYOffset = offset;
- }
+ yOffset = offset;
+
if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
wallpaperWin.mWallpaperY = wpy;
wallpaperWin.mWallpaperYStep = wpys;
rawChanged = true;
}
+ boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset);
+
if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
try {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 2ae5c7b..ddda027 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -74,10 +74,6 @@
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
- final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- winAnimator.computeShownFrameLocked();
- // No need to lay out the windows - we can just set the wallpaper position directly.
- winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
// We only want to be synchronous with one wallpaper.
sync = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 20349b9..ab10197 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -226,13 +226,6 @@
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
- final int numDisplays = mDisplayContentsAnimators.size();
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- dc.onPendingTransactionApplied();
- }
-
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
@@ -266,7 +259,6 @@
}
mService.destroyPreservedSurfaceLocked();
- mService.mWindowPlacerLocked.destroyPendingSurfaces();
executeAfterPrepareSurfacesRunnables();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 42f6065..b7525c0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -109,8 +109,7 @@
WindowContainer(WindowManagerService service) {
mService = service;
mPendingTransaction = service.mTransactionFactory.make();
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
- service.mAnimator::addAfterPrepareSurfacesRunnable, service);
+ mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
}
@Override
@@ -286,8 +285,9 @@
}
if (mSurfaceControl != null) {
- destroyAfterPendingTransaction(mSurfaceControl);
+ getPendingTransaction().destroy(mSurfaceControl);
mSurfaceControl = null;
+ scheduleAnimation();
}
if (mParent != null) {
@@ -1079,19 +1079,6 @@
return mSurfaceControl;
}
- /**
- * Destroy a given surface after executing mPendingTransaction. This is
- * largely a workaround for destroy not being part of transactions
- * rather than an intentional design, so please take care when
- * expanding use.
- */
- @Override
- public void destroyAfterPendingTransaction(SurfaceControl surface) {
- if (mParent != null) {
- mParent.destroyAfterPendingTransaction(surface);
- }
- }
-
@Override
public Transaction getPendingTransaction() {
return mPendingTransaction;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1521afc..0c6429a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -100,15 +100,15 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.WindowManagerServiceProto.APP_TRANSITION;
-import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAY_FROZEN;
-import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_APP;
-import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION;
-import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY;
-import static com.android.server.wm.proto.WindowManagerServiceProto.ROOT_WINDOW_CONTAINER;
-import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.APP_TRANSITION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_APP;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.LAST_ORIENTATION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.POLICY;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROTATION;
import android.Manifest;
import android.Manifest.permission;
@@ -822,9 +822,6 @@
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
synchronized (mWindowMap) {
- if (mRoot.mSurfaceTraceEnabled) {
- mRoot.mRemoteEventTrace.openSurfaceTransaction();
- }
SurfaceControl.openTransaction();
}
} finally {
@@ -843,9 +840,6 @@
try {
traceStateLocked(where);
} finally {
- if (mRoot.mSurfaceTraceEnabled) {
- mRoot.mRemoteEventTrace.closeSurfaceTransaction();
- }
SurfaceControl.closeTransaction();
}
}
@@ -1384,14 +1378,8 @@
win.attach();
mWindowMap.put(client.asBinder(), win);
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
- win.getOwningPackage());
- if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
- (startOpResult != AppOpsManager.MODE_DEFAULT)) {
- win.setAppOpVisibilityLw(false);
- }
- }
+
+ win.initAppOpsState();
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
@@ -1595,30 +1583,6 @@
return false;
}
- @Override
- public void enableSurfaceTrace(ParcelFileDescriptor pfd) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != SHELL_UID && callingUid != ROOT_UID) {
- throw new SecurityException("Only shell can call enableSurfaceTrace");
- }
-
- synchronized (mWindowMap) {
- mRoot.enableSurfaceTrace(pfd);
- }
- }
-
- @Override
- public void disableSurfaceTrace() {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != SHELL_UID && callingUid != ROOT_UID &&
- callingUid != SYSTEM_UID) {
- throw new SecurityException("Only shell can call disableSurfaceTrace");
- }
- synchronized (mWindowMap) {
- mRoot.disableSurfaceTrace();
- }
- }
-
/**
* Set mScreenCaptureDisabled for specific user
*/
@@ -1656,9 +1620,8 @@
void postWindowRemoveCleanupLocked(WindowState win) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
- }
+
+ win.resetAppOpsState();
if (mCurrentFocus == null) {
mWinRemovedSinceNullFocus.add(win);
@@ -2701,7 +2664,6 @@
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) {
synchronized (mWindowMap) {
- cancelRecentsAnimation();
mRecentsAnimationController = new RecentsAnimationController(this,
recentsAnimationRunner, callbacks, displayId);
mRecentsAnimationController.initialize();
@@ -2726,12 +2688,12 @@
}
public void cancelRecentsAnimation() {
- synchronized (mWindowMap) {
- if (mRecentsAnimationController != null) {
- // This call will call through to cleanupAnimation() below after the animation is
- // canceled
- mRecentsAnimationController.cancelAnimation();
- }
+ // Note: Do not hold the WM lock, this will lock appropriately in the call which also
+ // calls through to AM/RecentsAnimation.onAnimationFinished()
+ if (mRecentsAnimationController != null) {
+ // This call will call through to cleanupAnimation() below after the animation is
+ // canceled
+ mRecentsAnimationController.cancelAnimation();
}
}
@@ -6195,7 +6157,7 @@
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
- * {@link com.android.server.wm.proto.WindowManagerServiceProto}.
+ * {@link com.android.server.wm.proto.WindowManagerServiceDumpProto}.
*
* @param proto Stream to write the WindowContainer object to.
* @param trim If true, reduce the amount of data written.
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index e24c393..ab139db 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -74,8 +74,6 @@
return runSetScreenCapture(pw);
case "dismiss-keyguard":
return runDismissKeyguard(pw);
- case "surface-trace":
- return runSurfaceTrace(pw);
case "tracing":
// XXX this should probably be changed to use openFileForSystem() to create
// the output trace file, so the shell gets the correct semantics for where
@@ -235,46 +233,6 @@
return 0;
}
- private int runSurfaceTrace(PrintWriter pw) throws RemoteException {
- final ParcelFileDescriptor pfd;
- try {
- pfd = ParcelFileDescriptor.dup(getOutFileDescriptor());
- } catch (IOException e) {
- getErrPrintWriter().println("Unable to dup output stream: " + e.getMessage());
- return -1;
- }
- mInternal.enableSurfaceTrace(pfd);
-
- // Read input until an explicit quit command is sent or the stream is closed (meaning
- // the user killed the command).
- try {
- InputStream input = getRawInputStream();
- InputStreamReader converter = new InputStreamReader(input);
- BufferedReader in = new BufferedReader(converter);
- String line;
-
- while ((line = in.readLine()) != null) {
- if (line.length() <= 0) {
- // no-op
- } else if ("q".equals(line) || "quit".equals(line)) {
- break;
- } else {
- pw.println("Invalid command: " + line);
- }
- }
- } catch (IOException e) {
- e.printStackTrace(pw);
- } finally {
- mInternal.disableSurfaceTrace();
- try {
- pfd.close();
- } catch (IOException e) {
- }
- }
-
- return 0;
- }
-
private int parseDimension(String s) throws NumberFormatException {
if (s.endsWith("px")) {
return Integer.parseInt(s.substring(0, s.length() - 2));
@@ -311,8 +269,6 @@
pw.println(" Enable or disable screen capture.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
- pw.println(" surface-trace");
- pw.println(" Log surface commands to stdout in a binary format.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b706096..c5b270e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_NONE;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -138,7 +141,6 @@
import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS;
import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
@@ -298,12 +300,6 @@
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
- * Actual position of the surface shown on-screen (may be modified by animation). These are
- * in the screen's coordinate space (WITH the compatibility scale applied).
- */
- final Point mShownPosition = new Point();
-
- /**
* Insets that determine the actually visible area. These are in the application's
* coordinate space (without compatibility scale applied).
*/
@@ -462,10 +458,6 @@
int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
- // Wallpaper windows: pixels offset based on above variables.
- int mXOffset;
- int mYOffset;
-
/**
* This is set after IWindowSession.relayout() has been called at
* least once for the window. It allows us to detect the situation
@@ -771,8 +763,6 @@
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
- mXOffset = 0;
- mYOffset = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
@@ -1138,11 +1128,6 @@
}
@Override
- public Point getShownPositionLw() {
- return mShownPosition;
- }
-
- @Override
public Rect getDisplayFrameLw() {
return mDisplayFrame;
}
@@ -2571,7 +2556,7 @@
}
}
- public void setAppOpVisibilityLw(boolean state) {
+ private void setAppOpVisibilityLw(boolean state) {
if (mAppOpVisibility != state) {
mAppOpVisibility = state;
if (state) {
@@ -2588,6 +2573,49 @@
}
}
+ void initAppOpsState() {
+ if (mAppOp == OP_NONE || !mAppOpVisibility) {
+ return;
+ }
+ // If the app op was MODE_DEFAULT we would have checked the permission
+ // and add the window only if the permission was granted. Therefore, if
+ // the mode is MODE_DEFAULT we want the op to succeed as the window is
+ // shown.
+ final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+ getOwningUid(), getOwningPackage(), true);
+ if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+ setAppOpVisibilityLw(false);
+ }
+ }
+
+ void resetAppOpsState() {
+ if (mAppOp != OP_NONE && mAppOpVisibility) {
+ mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ }
+ }
+
+ void updateAppOpsState() {
+ if (mAppOp == OP_NONE) {
+ return;
+ }
+ final int uid = getOwningUid();
+ final String packageName = getOwningPackage();
+ if (mAppOpVisibility) {
+ // There is a race between the check and the finish calls but this is fine
+ // as this would mean we will get another change callback and will reconcile.
+ int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+ if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+ mService.mAppOps.finishOp(mAppOp, uid, packageName);
+ setAppOpVisibilityLw(false);
+ }
+ } else {
+ final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName);
+ if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
+ setAppOpVisibilityLw(true);
+ }
+ }
+ }
+
public void hidePermanentlyLw() {
if (!mPermanentlyHidden) {
mPermanentlyHidden = true;
@@ -3136,7 +3164,6 @@
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
- mShownPosition.writeToProto(proto, SHOWN_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
proto.write(ANIMATING_EXIT, mAnimatingExit);
for (int i = 0; i < mChildren.size(); i++) {
@@ -3252,10 +3279,6 @@
pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
}
- if (mXOffset != 0 || mYOffset != 0) {
- pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
- pw.print(" y="); pw.println(mYOffset);
- }
if (dumpAll) {
pw.print(prefix); pw.print("mGivenContentInsets=");
mGivenContentInsets.printShortString(pw);
@@ -3274,7 +3297,6 @@
pw.println(getLastReportedConfiguration());
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
if (dumpAll) {
@@ -4013,7 +4035,9 @@
final boolean hasSurface = mWinAnimator.hasSurface();
if (hasSurface) {
- mWinAnimator.hide("onExitAnimationDone");
+ // Use pendingTransaction here so hide is done the same transaction as the other
+ // animations when exiting
+ mWinAnimator.hide(getPendingTransaction(), "onExitAnimationDone");
}
// If we have an app token, we ask it to destroy the surface for us, so that it can take
@@ -4195,9 +4219,8 @@
final int width = mFrame.width();
final int height = mFrame.height();
- // Compute the offset of the window in relation to the decor rect.
- final int left = mXOffset + mFrame.left;
- final int top = mYOffset + mFrame.top;
+ final int left = mFrame.left;
+ final int top = mFrame.top;
// Initialize the decor rect to the entire frame.
if (isDockedResizing()) {
@@ -4299,14 +4322,9 @@
// When we change the Surface size, in scenarios which may require changing
// the surface position in sync with the resize, we use a preserved surface
// so we can freeze it while waiting for the client to report draw on the newly
- // sized surface. Don't preserve surfaces if the insets change while animating the pinned
- // stack since it can lead to issues if a new surface is created while calculating the
- // scale for the animation using the source hint rect
- // (see WindowStateAnimator#setSurfaceBoundariesLocked()).
- if (isDragResizeChanged()
- || (surfaceInsetsChanging() && !inPinnedWindowingMode())) {
- mLastSurfaceInsets.set(mAttrs.surfaceInsets);
-
+ // sized surface. At the moment this logic is only in place for switching
+ // in and out of the big surface for split screen resize.
+ if (isDragResizeChanged()) {
setDragResizing();
// We can only change top level windows to the full-screen surface when
// resizing (as we only have one full-screen surface). So there is no need
@@ -4391,8 +4409,8 @@
float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
- int x = mSurfacePosition.x + mShownPosition.x;
- int y = mSurfacePosition.y + mShownPosition.y;
+ int x = mSurfacePosition.x;
+ int y = mSurfacePosition.y;
// If changed, also adjust transformFrameToSurfacePosition
final WindowContainer parent = getParent();
@@ -4554,9 +4572,16 @@
}
transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+
if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
+ if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
+ mLastSurfaceInsets.set(mAttrs.surfaceInsets);
+ t.deferTransactionUntil(mSurfaceControl,
+ mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+ mAttrs.frameNumber);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9ce0537..13f05e0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -164,6 +164,8 @@
private boolean mAnimationStartDelayed;
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+
/** The pixel format of the underlying SurfaceControl */
int mSurfaceFormat;
@@ -209,6 +211,12 @@
float mExtraHScale = (float) 1.0;
float mExtraVScale = (float) 1.0;
+ // An offset in pixel of the surface contents from the window position. Used for Wallpaper
+ // to provide the effect of scrolling within a large surface. We just use these values as
+ // a cache.
+ int mXOffset = 0;
+ int mYOffset = 0;
+
private final Rect mTmpSize = new Rect();
private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction();
@@ -280,16 +288,21 @@
}
}
- void hide(String reason) {
+ void hide(SurfaceControl.Transaction transaction, String reason) {
if (!mLastHidden) {
//dump();
mLastHidden = true;
if (mSurfaceController != null) {
- mSurfaceController.hideInTransaction(reason);
+ mSurfaceController.hide(transaction, reason);
}
}
}
+ void hide(String reason) {
+ hide(mTmpTransaction, reason);
+ SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
+ }
+
boolean finishDrawingLocked() {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -438,7 +451,7 @@
flags |= SurfaceControl.SECURE;
}
- mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -679,8 +692,8 @@
// WindowState.prepareSurfaces expands for surface insets (in order they don't get
// clipped by the WindowState surface), so we need to go into the other direction here.
- tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
+ tmpMatrix.postTranslate(mWin.mAttrs.surfaceInsets.left,
+ mWin.mAttrs.surfaceInsets.top);
// "convert" it into SurfaceFlinger's format
@@ -695,9 +708,6 @@
mDtDx = tmpFloats[Matrix.MSKEW_Y];
mDtDy = tmpFloats[Matrix.MSKEW_X];
mDsDy = tmpFloats[Matrix.MSCALE_Y];
- float x = tmpFloats[Matrix.MTRANS_X];
- float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set(Math.round(x), Math.round(y));
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -707,8 +717,7 @@
mShownAlpha = mAlpha;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
- || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
- && x == frame.left && y == frame.top))) {
+ || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) {
//Slog.i(TAG_WM, "Applying alpha transform");
if (screenAnimation) {
mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
@@ -738,10 +747,6 @@
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- // WindowState.prepareSurfaces expands for surface insets (in order they don't get
- // clipped by the WindowState surface), so we need to go into the other direction here.
- mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
mShownAlpha = mAlpha;
mHaveMatrix = false;
mDsDx = mWin.mGlobalScale;
@@ -792,12 +797,6 @@
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
+ " fullscreen=" + fullscreen);
- if (isFreeformResizing && !w.isChildWindow()) {
- // For freeform resizing non child windows, we are using the big surface positioned
- // at 0,0. Thus we must express the crop in that coordinate space.
- clipRect.offset(w.mShownPosition.x, w.mShownPosition.y);
- }
-
w.expandForSurfaceInsets(clipRect);
// The clip rect was generated assuming (0,0) as the window origin,
@@ -834,7 +833,7 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
mExtraHScale = (float) 1.0;
@@ -870,17 +869,19 @@
float surfaceWidth = mSurfaceController.getWidth();
float surfaceHeight = mSurfaceController.getHeight();
+ final Rect insets = attrs.surfaceInsets;
+
if (isForceScaled()) {
- int hInsets = attrs.surfaceInsets.left + attrs.surfaceInsets.right;
- int vInsets = attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+ int hInsets = insets.left + insets.right;
+ int vInsets = insets.top + insets.bottom;
float surfaceContentWidth = surfaceWidth - hInsets;
float surfaceContentHeight = surfaceHeight - vInsets;
if (!mForceScaleUntilResize) {
mSurfaceController.forceScaleableInTransaction(true);
}
- int posX = mTmpSize.left;
- int posY = mTmpSize.top;
+ int posX = 0;
+ int posY = 0;
task.mStack.getDimBounds(mTmpStackBounds);
boolean allowStretching = false;
@@ -927,9 +928,19 @@
posX -= (int) (tw * mExtraHScale * mTmpSourceBounds.left);
posY -= (int) (th * mExtraVScale * mTmpSourceBounds.top);
- // Always clip to the stack bounds since the surface can be larger with the current
- // scale
- clipRect = null;
+ // In pinned mode the clip rectangle applied to us by our stack has been
+ // expanded outwards to allow for shadows. However in case of source bounds set
+ // we need to crop to within the surface. The code above has scaled and positioned
+ // the surface to fit the unexpanded stack bounds, but now we need to reapply
+ // the cropping that the stack would have applied if it weren't expanded. This
+ // can be different in each direction based on the source bounds.
+ clipRect = mTmpClipRect;
+ clipRect.set((int)((insets.left + mTmpSourceBounds.left) * tw),
+ (int)((insets.top + mTmpSourceBounds.top) * th),
+ insets.left + (int)(surfaceWidth
+ - (tw* (surfaceWidth - mTmpSourceBounds.right))),
+ insets.top + (int)(surfaceHeight
+ - (th * (surfaceHeight - mTmpSourceBounds.bottom))));
} else {
// We want to calculate the scaling based on the content area, not based on
// the entire surface, so that we scale in sync with windows that don't have insets.
@@ -955,8 +966,8 @@
// non inset content at the same position, we have to shift the whole window
// forward. Likewise for scaling up, we've increased this distance, and we need
// to shift by a negative number to compensate.
- posX += attrs.surfaceInsets.left * (1 - mExtraHScale);
- posY += attrs.surfaceInsets.top * (1 - mExtraVScale);
+ posX += insets.left * (1 - mExtraHScale);
+ posY += insets.top * (1 - mExtraVScale);
mSurfaceController.setPositionInTransaction((float) Math.floor(posX),
(float) Math.floor(posY), recoveringMemory);
@@ -970,8 +981,7 @@
mForceScaleUntilResize = true;
} else {
if (!w.mSeamlesslyRotated) {
- mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
- recoveringMemory);
+ mSurfaceController.setPositionInTransaction(0, 0, recoveringMemory);
}
}
@@ -1139,24 +1149,26 @@
mSurfaceController.setTransparentRegionHint(region);
}
- void setWallpaperOffset(Point shownPosition) {
- final LayoutParams attrs = mWin.getAttrs();
- final int left = shownPosition.x - attrs.surfaceInsets.left;
- final int top = shownPosition.y - attrs.surfaceInsets.top;
+ boolean setWallpaperOffset(int dx, int dy) {
+ if (mXOffset == dx && mYOffset == dy) {
+ return false;
+ }
+ mXOffset = dx;
+ mYOffset = dy;
try {
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
mService.openSurfaceTransaction();
- mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
- mWin.mFrame.top + top, false);
+ mSurfaceController.setPositionInTransaction(dx, dy, false);
applyCrop(null, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
- + " pos=(" + left + "," + top + ")", e);
+ + " pos=(" + dx + "," + dy + ")", e);
} finally {
mService.closeSurfaceTransaction("setWallpaperOffset");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setWallpaperOffset");
+ return true;
}
}
@@ -1451,22 +1463,6 @@
DsDy * w.mVScale, false);
}
- void enableSurfaceTrace(FileDescriptor fd) {
- if (mSurfaceController != null) {
- mSurfaceController.installRemoteTrace(fd);
- }
- }
-
- void disableSurfaceTrace() {
- if (mSurfaceController != null) {
- try {
- mSurfaceController.removeRemoteTrace();
- } catch (ClassCastException e) {
- Slog.e(TAG, "Disable surface trace for " + this + " but its not enabled");
- }
- }
- }
-
/** The force-scaled state for a given window can persist past
* the state for it's stack as the windows complete resizing
* independently of one another.
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 554a600..9d6f8f7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -86,6 +86,8 @@
private final int mWindowType;
private final Session mWindowSession;
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+
public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator;
@@ -110,21 +112,8 @@
.setMetadata(windowType, ownerUid);
mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-
- if (mService.mRoot.mSurfaceTraceEnabled) {
- installRemoteTrace(mService.mRoot.mSurfaceTraceFd.getFileDescriptor());
- }
}
- void installRemoteTrace(FileDescriptor fd) {
- mSurfaceControl = new RemoteSurfaceTrace(fd, mSurfaceControl, mAnimator.mWin);
- }
-
- void removeRemoteTrace() {
- mSurfaceControl = new SurfaceControl(mSurfaceControl);
- }
-
-
private void logSurface(String msg, RuntimeException where) {
String str = " SURFACE " + msg + ": " + title;
if (where != null) {
@@ -148,21 +137,23 @@
}
}
- void hideInTransaction(String reason) {
+ void hide(SurfaceControl.Transaction transaction, String reason) {
if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
mHiddenForOtherReasons = true;
mAnimator.destroyPreservedSurfaceLocked();
- updateVisibility();
+ if (mSurfaceShown) {
+ hideSurface(transaction);
+ }
}
- private void hideSurface() {
+ private void hideSurface(SurfaceControl.Transaction transaction) {
if (mSurfaceControl == null) {
return;
}
setShown(false);
try {
- mSurfaceControl.hide();
+ transaction.hide(mSurfaceControl);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception hiding surface in " + this);
}
@@ -421,7 +412,8 @@
private boolean updateVisibility() {
if (mHiddenForCrop || mHiddenForOtherReasons) {
if (mSurfaceShown) {
- hideSurface();
+ hideSurface(mTmpTransaction);
+ SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
}
return false;
} else {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 286cc49..4179590 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -103,7 +103,6 @@
}
private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
- private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
private final Runnable mPerformSurfacePlacement;
@@ -702,25 +701,6 @@
}
}
- /**
- * Puts the {@param surface} into a pending list to be destroyed after the current transaction
- * has been committed.
- */
- void destroyAfterTransaction(SurfaceControl surface) {
- mPendingDestroyingSurfaces.add(surface);
- }
-
- /**
- * Destroys any surfaces that have been put into the pending list with
- * {@link #destroyAfterTransaction}.
- */
- void destroyPendingSurfaces() {
- for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
- mPendingDestroyingSurfaces.get(i).destroy();
- }
- mPendingDestroyingSurfaces.clear();
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled);
pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5b5de0e..2f6a6e0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -23,6 +23,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
@@ -331,6 +332,8 @@
// The system server should never make non-oneway calls
Binder.setWarnOnBlocking(true);
+ // The system server should always load safe labels
+ PackageItemInfo.setForceSafeLabels(true);
// Deactivate SQLiteCompatibilityWalFlags until settings provider is initialized
SQLiteCompatibilityWalFlags.init(null);
@@ -839,7 +842,8 @@
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
- ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
+ ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
+ /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
traceEnd();
traceBeginAndSlog("SetWindowManagerService");
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
index 5e3c974..3d2d8af 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +31,7 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;
import org.junit.Before;
@@ -45,7 +47,6 @@
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class TransportClientManagerTest {
-
private static final String PACKAGE_NAME = "random.package.name";
private static final String CLASS_NAME = "random.package.name.transport.Transport";
@@ -72,14 +73,11 @@
}
@Test
- public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
- Bundle extras = new Bundle();
- extras.putBoolean("random_extra", true);
- mBindIntent.putExtras(extras);
-
+ public void testGetTransportClient() {
TransportClient transportClient =
- mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
+ mTransportClientManager.getTransportClient(mTransportComponent, "caller");
+ // Connect to be able to extract the intent
transportClient.connectAsync(mTransportConnectionListener, "caller");
verify(mContext)
.bindServiceAsUser(
@@ -89,6 +87,35 @@
any(UserHandle.class));
}
+ @Test
+ public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
+ Bundle extras = new Bundle();
+ extras.putBoolean("random_extra", true);
+
+ TransportClient transportClient =
+ mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
+
+ transportClient.connectAsync(mTransportConnectionListener, "caller");
+ mBindIntent.putExtras(extras);
+ verify(mContext)
+ .bindServiceAsUser(
+ argThat(matchesIntentAndExtras(mBindIntent)),
+ any(ServiceConnection.class),
+ anyInt(),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void testDisposeOfTransportClient() {
+ TransportClient transportClient =
+ spy(mTransportClientManager.getTransportClient(mTransportComponent, "caller"));
+
+ mTransportClientManager.disposeOfTransportClient(transportClient, "caller");
+
+ verify(transportClient).unbind(any());
+ verify(transportClient).markAsDisposed();
+ }
+
private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
return (Intent actualIntent) -> {
if (!expectedIntent.filterEquals(actualIntent)) {
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index ff1644c..5b65473 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -26,16 +26,21 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
+import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.expectThrows;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -43,10 +48,9 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
-import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
import com.android.server.testing.SystemLoaderPackages;
+import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowCloseGuard;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSlog;
@@ -61,11 +65,19 @@
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
sdk = 26,
- shadows = {ShadowEventLog.class, ShadowCloseGuard.class, ShadowSlog.class}
+ shadows = {
+ ShadowEventLog.class,
+ ShadowCloseGuard.class,
+ ShadowSlog.class,
+ FrameworkShadowLooper.class
+ }
)
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
@@ -80,14 +92,16 @@
private ComponentName mTransportComponent;
private String mTransportString;
private Intent mBindIntent;
- private ShadowLooper mShadowLooper;
+ private FrameworkShadowLooper mShadowMainLooper;
+ private ShadowLooper mShadowWorkerLooper;
+ private Handler mWorkerHandler;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Looper mainLooper = Looper.getMainLooper();
- mShadowLooper = shadowOf(mainLooper);
+ mShadowMainLooper = extract(mainLooper);
mTransportComponent =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
mTransportString = mTransportComponent.flattenToShortString();
@@ -107,6 +121,11 @@
anyInt(),
any(UserHandle.class)))
.thenReturn(true);
+
+ HandlerThread workerThread = new HandlerThread("worker");
+ workerThread.start();
+ mShadowWorkerLooper = shadowOf(workerThread.getLooper());
+ mWorkerHandler = workerThread.getThreadHandler();
}
@Test
@@ -129,12 +148,11 @@
@Test
public void testConnectAsync_callsListenerWhenConnected() throws Exception {
mTransportClient.connectAsync(mTransportConnectionListener, "caller");
-
- // Simulate framework connecting
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+
connection.onServiceConnected(mTransportComponent, mTransportBinder);
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
.onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
}
@@ -148,8 +166,7 @@
mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
connection.onServiceConnected(mTransportComponent, mTransportBinder);
-
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
.onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
verify(mTransportConnectionListener2)
@@ -164,7 +181,7 @@
mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener2)
.onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
}
@@ -180,7 +197,7 @@
mTransportClient.connectAsync(mTransportConnectionListener, "caller");
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
.onTransportConnectionResult(isNull(), eq(mTransportClient));
}
@@ -233,11 +250,11 @@
@Test
public void testConnectAsync_callsListenerIfBindingDies() throws Exception {
mTransportClient.connectAsync(mTransportConnectionListener, "caller");
-
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+
connection.onBindingDied(mTransportComponent);
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
.onTransportConnectionResult(isNull(), eq(mTransportClient));
}
@@ -251,8 +268,7 @@
mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
connection.onBindingDied(mTransportComponent);
-
- mShadowLooper.runToEndOfTasks();
+ mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
.onTransportConnectionResult(isNull(), eq(mTransportClient));
verify(mTransportConnectionListener2)
@@ -271,9 +287,9 @@
@Test
public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
ShadowEventLog.setUp();
-
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+
connection.onServiceConnected(mTransportComponent, mTransportBinder);
assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
@@ -283,9 +299,9 @@
@Test
public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
ShadowEventLog.setUp();
-
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+
connection.onBindingDied(mTransportComponent);
assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
@@ -293,6 +309,66 @@
}
@Test
+ public void testConnect_whenConnected_returnsTransport() throws Exception {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mTransportBinder);
+
+ IBackupTransport transportBinder =
+ runInWorkerThread(() -> mTransportClient.connect("caller2"));
+
+ assertThat(transportBinder).isNotNull();
+ }
+
+ @Test
+ public void testConnect_afterOnServiceDisconnected_returnsNull() throws Exception {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mTransportBinder);
+ connection.onServiceDisconnected(mTransportComponent);
+
+ IBackupTransport transportBinder =
+ runInWorkerThread(() -> mTransportClient.connect("caller2"));
+
+ assertThat(transportBinder).isNull();
+ }
+
+ @Test
+ public void testConnect_afterOnBindingDied_returnsNull() throws Exception {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onBindingDied(mTransportComponent);
+
+ IBackupTransport transportBinder =
+ runInWorkerThread(() -> mTransportClient.connect("caller2"));
+
+ assertThat(transportBinder).isNull();
+ }
+
+ @Test
+ public void testConnect_callsThroughToConnectAsync() throws Exception {
+ // We can't mock bindServiceAsUser() instead of connectAsync() and call the listener inline
+ // because in our code in TransportClient we assume this is NOT run inline, such that the
+ // reentrant lock can't be acquired by the listener at the call-site of bindServiceAsUser(),
+ // which is what would happened if we mocked bindServiceAsUser() to call the listener
+ // inline.
+ TransportClient transportClient = spy(mTransportClient);
+ doAnswer(
+ invocation -> {
+ TransportConnectionListener listener = invocation.getArgument(0);
+ listener.onTransportConnectionResult(mTransportBinder, transportClient);
+ return null;
+ })
+ .when(transportClient)
+ .connectAsync(any(), any());
+
+ IBackupTransport transportBinder =
+ runInWorkerThread(() -> transportClient.connect("caller"));
+
+ assertThat(transportBinder).isNotNull();
+ }
+
+ @Test
public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
@@ -447,6 +523,19 @@
assertThat(ShadowCloseGuard.hasReported()).isFalse();
}
+ @Nullable
+ private <T> T runInWorkerThread(Supplier<T> supplier) throws Exception {
+ CompletableFuture<T> future = new CompletableFuture<>();
+ mWorkerHandler.post(() -> future.complete(supplier.get()));
+ // Although we are using a separate looper, we are still calling runToEndOfTasks() in the
+ // main thread (Robolectric only *simulates* multi-thread). The only option left is to fool
+ // the caller.
+ mShadowMainLooper.setCurrentThread(false);
+ mShadowWorkerLooper.runToEndOfTasks();
+ mShadowMainLooper.reset();
+ return future.get();
+ }
+
private void assertEventLogged(int tag, Object... values) {
assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
new file mode 100644
index 0000000..c0eeb38
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing.shadows;
+
+import android.os.Looper;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.Optional;
+
+@Implements(value = Looper.class, inheritImplementationMethods = true)
+public class FrameworkShadowLooper extends ShadowLooper {
+ @RealObject private Looper mLooper;
+ private Optional<Boolean> mIsCurrentThread = Optional.empty();
+
+ public void setCurrentThread(boolean currentThread) {
+ mIsCurrentThread = Optional.of(currentThread);
+ }
+
+ public void reset() {
+ mIsCurrentThread = Optional.empty();
+ }
+
+ @Implementation
+ public boolean isCurrentThread() {
+ if (mIsCurrentThread.isPresent()) {
+ return mIsCurrentThread.get();
+ }
+ return Thread.currentThread() == mLooper.getThread();
+ }
+}
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 fb1595e..f2f1b10 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -111,8 +111,6 @@
assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
}
- // TODO: b/71582913
- @Ignore("b/71582913")
@Test
public void testPausingWhenVisibleFromStopped() throws Exception {
final MutableBoolean pauseFound = new MutableBoolean(false);
@@ -204,4 +202,20 @@
verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
anyInt(), anyInt(), eq(record.info));
}
+
+ @Test
+ public void testFinishingAfterDestroying() throws Exception {
+ assertFalse(mActivity.finishing);
+ mActivity.setState(DESTROYING, "testFinishingAfterDestroying");
+ assertTrue(mActivity.isState(DESTROYING));
+ assertTrue(mActivity.finishing);
+ }
+
+ @Test
+ public void testFinishingAfterDestroyed() throws Exception {
+ assertFalse(mActivity.finishing);
+ mActivity.setState(DESTROYED, "testFinishingAfterDestroyed");
+ assertTrue(mActivity.isState(DESTROYED));
+ assertTrue(mActivity.finishing);
+ }
}
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 c62820e..a98d5a1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -47,7 +47,7 @@
* Tests for the {@link ActivityStack} class.
*
* Build/Install/Run:
- * atest ActivityStackTests
+ * atest FrameworksServicesTests:com.android.server.am.ActivityStackTests
*/
@SmallTest
@Presubmit
@@ -425,6 +425,31 @@
assertTrue(display.getStackAboveHome() == fullscreenStack2);
}
+ @Test
+ public void testSplitScreenMoveToFront() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+ splitScreenPrimary.setIsTranslucent(false);
+ splitScreenSecondary.setIsTranslucent(false);
+ assistantStack.setIsTranslucent(false);
+
+ assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+
+ splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
+
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+ }
+
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
final T stack = display.createStack(windowingMode, activityType, onTop);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 501f966..b4f8474 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -338,6 +338,26 @@
assertFalse(event.isDefaultBrightnessConfig);
assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
assertFalse(event.isUserSetBrightness);
+
+ // Pretend user 1 is a profile of user 0.
+ mInjector.mProfiles = new int[]{0, 1};
+ events = tracker.getEvents(0, true).getList();
+ // Both events should now be returned.
+ assertEquals(2, events.size());
+ BrightnessChangeEvent userZeroEvent;
+ BrightnessChangeEvent userOneEvent;
+ if (events.get(0).userId == 0) {
+ userZeroEvent = events.get(0);
+ userOneEvent = events.get(1);
+ } else {
+ userZeroEvent = events.get(1);
+ userOneEvent = events.get(0);
+ }
+ assertEquals(0, userZeroEvent.userId);
+ assertEquals("com.example.app", userZeroEvent.packageName);
+ assertEquals(1, userOneEvent.userId);
+ // Events from user 1 should have the package name redacted
+ assertNull(userOneEvent.packageName);
}
@Test
@@ -597,6 +617,7 @@
Handler mHandler;
boolean mIdleScheduled;
boolean mInteractive = true;
+ int[] mProfiles;
public TestInjector(Handler handler) {
mHandler = handler;
@@ -682,6 +703,15 @@
}
@Override
+ public int[] getProfileIds(UserManager userManager, int userId) {
+ if (mProfiles != null) {
+ return mProfiles;
+ } else {
+ return new int[]{userId};
+ }
+ }
+
+ @Override
public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
ActivityManager.StackInfo focusedStack = new ActivityManager.StackInfo();
focusedStack.userId = 0;
@@ -689,15 +719,18 @@
return focusedStack;
}
+ @Override
public void scheduleIdleJob(Context context) {
// Don't actually schedule jobs during unit tests.
mIdleScheduled = true;
}
+ @Override
public void cancelIdleJob(Context context) {
mIdleScheduled = false;
}
+ @Override
public boolean isInteractive(Context context) {
return mInteractive;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index e6a36c6..0ceb558 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -295,6 +295,18 @@
}
@Test
+ public void initRecoveryService_throwsIfInvalidCert() throws Exception {
+ byte[] modifiedCertXml = TestData.getCertXml();
+ modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
+ try {
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, modifiedCertXml);
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("validate cert");
+ }
+ }
+
+ @Test
public void initRecoveryService_updatesWithLargerSerial() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -355,6 +367,70 @@
}
@Test
+ public void initRecoveryServiceWithSigFile_succeeds() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_1);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
+ }
+
+ @Test
+ public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
+ TestData.getSigXml());
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
+ @Test
+ public void initRecoveryServiceWithSigFile_throwsIfNullSigFile() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
+ /*recoveryServiceSigFile=*/ null);
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
+ @Test
+ public void initRecoveryServiceWithSigFile_throwsIfWrongSigFileFormat() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
+ getUtf8Bytes("wrong-sig-file-format"));
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("parse the sig file");
+ }
+ }
+
+ @Test
+ public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception {
+ byte[] modifiedCertXml = TestData.getCertXml();
+ modifiedCertXml[modifiedCertXml.length - 1] = 0; // Change the last new line char to a zero
+ try {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ ROOT_CERTIFICATE_ALIAS, modifiedCertXml, TestData.getSigXml());
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("is invalid");
+ }
+ }
+
+ @Test
public void startRecoverySession_checksPermissionFirst() throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 0e4f91b..b5d6ce8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -9,6 +9,7 @@
public final class TestData {
+ private static final long DEFAULT_SERIAL = 1000;
private static final String CERT_PATH_ENCODING = "PkiPath";
private static final String CERT_PATH_1_BASE64 = ""
@@ -89,10 +90,11 @@
private static final String THM_CERT_XML_BEFORE_SERIAL = ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<certificates>\n"
- + " <metadata>\n"
- + " <serial>\n";
- private static final String THM_CERT_XML_AFTER_SERIAL = ""
+ + "<certificates>\n"
+ + " <metadata>\n"
+ + " <serial>\n"
+ + " ";
+ private static final String THM_CERT_XML_AFTER_SERIAL = "\n"
+ " </serial>\n"
+ " <creation-time>\n"
+ " 1515697631\n"
@@ -163,6 +165,53 @@
+ " </cert>\n"
+ " </endpoints>\n"
+ "</certificates>\n";
+ private static final String THM_SIG_XML = ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<signature>\n"
+ + " <intermediates>\n"
+ + " </intermediates>\n"
+ + " <certificate>\n"
+ + " MIIFLzCCAxegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v\n"
+ + " Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDIwMzAwNDIwM1oXDTI4MDIwMTAwNDIw\n"
+ + " M1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0\n"
+ + " ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANyQeJvRfqtDIOqTjnX1\n"
+ + " vl27Q6sI+TfRdcrDP4fPnLhwZlpYoZwc4dZLZf1gClHM7TT8Ru8WRZVRNUbbvAnn\n"
+ + " hX4LccdI4BRYeESB8Va+8fB+f0dMPHUESTv1pConsO4nTpKf9Y6Iy0pUBPnoyLyZ\n"
+ + " 6QHGkw6t1mrCVwutRWxnEQezDn9x5m7hxJbNztLOWds0rVwKDJEMapZ/oaneEYTz\n"
+ + " qRQ60zWL3lGBQinD7D/PTDGkXqQjJBOMr4qOJgf9EE4kgRybqxJZmUyi0otKfaWF\n"
+ + " /5cvzJTETEgdOix95vTvtBZbjDYEHY1kzjA8A7fDhrDfcU2KANBzZJBiadQRiYhw\n"
+ + " PyTHvv4lYKALEElzbNMde0HFa5cBD6J6C3xE75AP3ul3pcoz3E+wA6RxYunv2j4A\n"
+ + " miXg/l/C+Bgzr2YziXAfXa/zpEjhqm09A5qDQoMgdfIpuNpDICC/fSXgqQOl/a5Y\n"
+ + " 4OE45PA1tU9hSbexhSWEFxvKFOTxio32w9ThABqjmwpMBhmAH+aWF5DKlNTDK+rP\n"
+ + " ptHMZ6FUHeW0/2ALIp0jThVcRS5QFaTdJPJXZERB6fn8soCezq/XULPbZmsR0K80\n"
+ + " uB62fJXsImw5r/AP8aJRYvZNrunOyGL1qEmqutw09FsDM4tw0pmQV7GHM+mie6j+\n"
+ + " k2txpF1rV+t6lfFWSZuyA5QvAgMBAAGjZjBkMB0GA1UdDgQWBBR3qZ3aEI+WZ/dW\n"
+ + " QRfkV8PEoEttoDAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DASBgNV\n"
+ + " HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC\n"
+ + " AgEAWBKGG5b5ZVOjA3TzQ8t/RNY+UcAIcdQ+CF/usUbGBSMnKkKWgTzgF6DdE6k/\n"
+ + " mXX0IOpn07I/MDXPpUDN4ZfIWFm9vLsba1kMFE/+/S7ymdIB0LkXyXxWHZgZ2ATe\n"
+ + " xkPbm+l0GLs/QR/othB604lefzo+qCaBbUXqXPEOMoKyL2Sl5TQBbAN05WDvjzCS\n"
+ + " 7nSjxQTOxXDHMV322gaWB7+efcUfknsS4bMCUXBOgeqnBNUR6BoB3SX2avn6tLtS\n"
+ + " t3dfzQEcOoIFxcTtSpu3PDlj6WajAwKdbSQU3TZ0L10u3Zz0jEL72w8gDl5OStkG\n"
+ + " SuRNVEYuPavb2PucL2blVopwGpwtkNbLylGdrzJpmmuA10Tx39ZNj2dWyJzT03zM\n"
+ + " 0XlDRuGFZrdCieUH1dblmoOC6JyW+f7lO1ETNIC1I7LEz6B8beXTCM7LAiTVoVrn\n"
+ + " ZFqoBY8vrso4zobe8o2kydbmGuYeuKO4rGJqsYihl+RkxHmKMrS4WC/f+no0cv9h\n"
+ + " AFockmGQp63lOGI/HBb37WT9lxdV5jbpeH7bYan/y2jtlbyC48+I3WF+kP7t/Uh1\n"
+ + " KYcOuxlBDtt/NXRummzI00Ht7YRzLD4ZCfLbufK0EskMZ1EG6tvBC/OmlA7pCaWT\n"
+ + " St8KzOHtsYKgJA3Fea4OxDkEL6lBSommVOp2zWybKLb3Gzc=\n"
+ + " </certificate>\n"
+ + " <value>\n"
+ + " uKJ4W8BPCdVaIBe2ZiMxxk5L5vGBV9QwaOEGU80LgtA/gEqkiO2IMUBlQJFqvvhh6RSph5lWpLuv\n"
+ + " /Xt7WBzDsZOcxXNffg2+pWNpbpwZdHohlwQEI1OqiVYVnfG4euAkzeWZZLsRUuAjHfcWVIzDoSoK\n"
+ + " wC+gqdUQHBV+pWyn6PXVslS0JIldeegbiwF076M1D7ybeCABXoQelSZRHkx1szO8UnxSR3X7Cemu\n"
+ + " p9De/7z9+WPPclqybINVIPy6Kvl8mHrGSlzawQRDKtoMrJa8bo93PookF8sbg5EoGapV0yNpMEiA\n"
+ + " spq3DEcdXB6mGDGPnLbS2WXq4zjKopASRKkZvOMdgfS6NdUMDtKS1TsOrv2KKTkLnGYfvdAeWiMg\n"
+ + " oFbuyYQ0mnDlLH1UW6anI8RxXn+wmdyZA+/ksapGvRmkvz0Mb997WzqNl7v7UTr0SU3Ws01hFsm6\n"
+ + " lW++MsotkyfpR9mWB8/dqVNVShLmIlt7U/YFVfziYSrVdjcAdIlgJ6Ihxb92liQHOU+Qr1YDOmm1\n"
+ + " JSnhlQVvFxWZG7hm5laNL6lqXz5VV6Gk5IeLtMb8kdHz3zj4ascdldapVPLJIa5741GNNgQNU0nH\n"
+ + " FhAyKk0zN7PbL1/XGWPU+s5lai4HE6JM2CKA7jE7cYrdaDZxbba+9iWzQ4YEBDr5Z3OoloK5dvs=\n"
+ + " </value>\n"
+ + "</signature>\n";
public static byte[] getCertPath1Bytes() {
try {
@@ -199,4 +248,12 @@
String xml = THM_CERT_XML_BEFORE_SERIAL + serial + THM_CERT_XML_AFTER_SERIAL;
return xml.getBytes(StandardCharsets.UTF_8);
}
+
+ public static byte[] getCertXml() {
+ return getCertXmlWithSerial(DEFAULT_SERIAL);
+ }
+
+ public static byte[] getSigXml() {
+ return THM_SIG_XML.getBytes(StandardCharsets.UTF_8);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
index 17a4d34..180345c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
@@ -27,7 +27,6 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +44,6 @@
.getBytes(StandardCharsets.UTF_8);
private static final byte[] KEY_MATERIAL = "app_key_data".getBytes(StandardCharsets.UTF_8);
private static final byte[] PUBLIC_KEY = "public_key_data".getBytes(StandardCharsets.UTF_8);
- private static final byte[] ACCOUNT = "test_account".getBytes(StandardCharsets.UTF_8);
private static final byte[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
private static final int SNAPSHOT_VERSION = 2;
private static final int MAX_ATTEMPTS = 10;
@@ -130,7 +128,6 @@
WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
.setAlias(ALIAS)
.setEncryptedKeyMaterial(KEY_MATERIAL)
- .setAccount(ACCOUNT)
.build();
writer.writeKeyEntry(entry);
@@ -142,7 +139,6 @@
WrappedApplicationKey copy = reader.readKeyEntry();
assertThat(copy.getAlias()).isEqualTo(ALIAS);
assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
- assertThat(copy.getAccount()).isEqualTo(ACCOUNT);
assertThrows(
IOException.class,
@@ -194,7 +190,6 @@
appKeysList.add(new WrappedApplicationKey.Builder()
.setAlias(ALIAS)
.setEncryptedKeyMaterial(KEY_MATERIAL)
- .setAccount(ACCOUNT)
.build());
KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
@@ -256,12 +251,10 @@
appKeysList.add(new WrappedApplicationKey.Builder()
.setAlias(ALIAS)
.setEncryptedKeyMaterial(KEY_MATERIAL)
- .setAccount(ACCOUNT)
.build());
appKeysList.add(new WrappedApplicationKey.Builder()
.setAlias(ALIAS2)
.setEncryptedKeyMaterial(KEY_MATERIAL)
- .setAccount(ACCOUNT)
.build());
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a628b7b..5de393c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -79,11 +79,6 @@
}
@Override
- public Point getShownPositionLw() {
- return new Point(parentFrame.left, parentFrame.top);
- }
-
- @Override
public Rect getDisplayFrameLw() {
return displayFrame;
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 30ca9ca..1d4348c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -77,7 +77,9 @@
public void setUpBase() throws Exception {
mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext());
mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.status_bar_height, STATUS_BAR_HEIGHT);
+ com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
mContext.getResourceMocker().addOverride(
com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
mContext.getResourceMocker().addOverride(
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index b55c79b..76e4e89 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.support.test.filters.FlakyTest;
import org.junit.Test;
import android.platform.test.annotations.Presubmit;
@@ -45,6 +46,7 @@
*/
@SmallTest
@Presubmit
+@FlakyTest(bugId = 74078662)
@org.junit.runner.RunWith(AndroidJUnit4.class)
public class AppWindowContainerControllerTests extends WindowTestsBase {
@@ -131,7 +133,6 @@
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
- mDisplayContent.onPendingTransactionApplied();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 64c3037..a120eba 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -23,11 +23,11 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.SurfaceControl;
@@ -39,7 +39,6 @@
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -47,7 +46,6 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
/**
* Test class for {@link SurfaceAnimatorTest}.
@@ -87,7 +85,7 @@
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ verify(mTransaction).destroy(eq(mAnimatable.mLeash));
// TODO: Verify reparenting once we use mPendingTransaction to reparent it back
}
@@ -97,7 +95,7 @@
final SurfaceControl firstLeash = mAnimatable.mLeash;
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
- assertTrue(mAnimatable.mPendingDestroySurfaces.contains(firstLeash));
+ verify(mTransaction).destroy(eq(firstLeash));
assertFalse(mAnimatable.mFinishedCallbackCalled);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -124,7 +122,7 @@
assertNotAnimating(mAnimatable);
verify(mSpec).onAnimationCancelled(any());
assertTrue(mAnimatable.mFinishedCallbackCalled);
- assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ verify(mTransaction).destroy(eq(mAnimatable.mLeash));
}
@Test
@@ -145,7 +143,7 @@
verifyZeroInteractions(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ verify(mTransaction).destroy(eq(mAnimatable.mLeash));
}
@Test
@@ -161,11 +159,11 @@
assertNotAnimating(mAnimatable);
assertAnimating(mAnimatable2);
assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
- assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash));
+ verify(mTransaction, never()).destroy(eq(leash));
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable2);
assertTrue(mAnimatable2.mFinishedCallbackCalled);
- assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash));
+ verify(mTransaction).destroy(eq(leash));
}
private void assertAnimating(MyAnimatable animatable) {
@@ -182,7 +180,6 @@
final SurfaceControl mParent;
final SurfaceControl mSurface;
- final ArrayList<SurfaceControl> mPendingDestroySurfaces = new ArrayList<>();
final SurfaceAnimator mSurfaceAnimator;
SurfaceControl mLeash;
boolean mFinishedCallbackCalled;
@@ -198,7 +195,7 @@
.build();
mFinishedCallbackCalled = false;
mLeash = null;
- mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, Runnable::run, sWm);
+ mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
}
@Override
@@ -219,11 +216,6 @@
}
@Override
- public void destroyAfterPendingTransaction(SurfaceControl surface) {
- mPendingDestroySurfaces.add(surface);
- }
-
- @Override
public Builder makeAnimationLeash() {
return new SurfaceControl.Builder(mSession) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index e78224c..41e446b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.DisplayInfo;
@@ -44,6 +45,7 @@
* bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
*/
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
@org.junit.runner.RunWith(AndroidJUnit4.class)
public class WindowConfigurationTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
index bab2170..502cb6e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -18,6 +18,7 @@
import android.app.WindowConfiguration;
import android.content.res.Configuration;
+import android.support.test.filters.FlakyTest;
import org.junit.Test;
import android.platform.test.annotations.Presubmit;
@@ -38,6 +39,7 @@
*/
@SmallTest
@Presubmit
+@FlakyTest(bugId = 74078662)
@org.junit.runner.RunWith(AndroidJUnit4.class)
public class WindowContainerControllerTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 1bd9a93..8446d25 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.support.test.filters.FlakyTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,6 +56,7 @@
*/
@SmallTest
@Presubmit
+@FlakyTest(bugId = 74078662)
@RunWith(AndroidJUnit4.class)
public class WindowContainerTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 74c72bf..83868d6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -21,6 +21,7 @@
import org.junit.runner.RunWith;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -52,6 +53,7 @@
* atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
*/
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowStateTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 7219104..394e636 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -20,6 +20,7 @@
import org.junit.runner.RunWith;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -40,6 +41,7 @@
* bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests
*/
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowTokenTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
index ad9aea7..a3ade1e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -31,6 +31,7 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.proto.ProtoOutputStream;
@@ -56,6 +57,7 @@
* bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
*/
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowTracingTest extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 5f44fb6..547be55 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -35,6 +35,7 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
+import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -53,6 +54,7 @@
* bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
*/
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ZOrderingTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
index d3cf978..6bbc7eb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertThat;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Pair;
@@ -37,6 +38,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@FlakyTest(bugId = 74078662)
@Presubmit
public class RotationCacheTest {
@@ -100,4 +102,4 @@
assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
assertThat(mComputationCalled, is(true));
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6144c51..6948b72 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -37,7 +38,12 @@
import android.content.Context;
import android.content.res.Resources;
import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioManagerInternal;
+import android.media.VolumePolicy;
import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -241,4 +247,119 @@
verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
}
+
+ @Test
+ public void testZenSetInternalRinger_AllPriorityNotificationSoundsMuted() {
+ AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+ mZenModeHelperSpy.mAudioManager = mAudioManager;
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
+ Integer.toString(AudioManager.RINGER_MODE_NORMAL));
+
+ // 1. Current ringer is normal
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowReminders = false;
+ mZenModeHelperSpy.mConfig.allowCalls = false;
+ mZenModeHelperSpy.mConfig.allowMessages = false;
+ mZenModeHelperSpy.mConfig.allowEvents = false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+
+ // 2. apply priority only zen - verify ringer is set to silent
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+ mZenModeHelperSpy.TAG);
+
+ // 3. apply zen off - verify zen is set to prevoius ringer (normal)
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
+ mZenModeHelperSpy.TAG);
+ }
+
+ @Test
+ public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() {
+ AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+ mZenModeHelperSpy.mAudioManager = mAudioManager;
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
+ Integer.toString(AudioManager.RINGER_MODE_NORMAL));
+
+ // 1. Current ringer is normal
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowReminders = true;
+
+ // 2. apply priority only zen - verify ringer is normal
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
+ mZenModeHelperSpy.TAG);
+
+ // 3. apply zen off - verify ringer remains normal
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
+ mZenModeHelperSpy.TAG);
+ }
+
+ @Test
+ public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartSilent() {
+ AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+ mZenModeHelperSpy.mAudioManager = mAudioManager;
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
+ Integer.toString(AudioManager.RINGER_MODE_SILENT));
+
+ // 1. Current ringer is silent
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowReminders = true;
+
+ // 2. apply priority only zen - verify ringer is silent
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+ mZenModeHelperSpy.TAG);
+
+ // 3. apply zen-off - verify ringer is still silent
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+ mZenModeHelperSpy.TAG);
+ }
+
+ @Test
+ public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_RingerChanges() {
+ AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+ mZenModeHelperSpy.mAudioManager = mAudioManager;
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
+ Integer.toString(AudioManager.RINGER_MODE_NORMAL));
+
+ // 1. Current ringer is normal
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowReminders = true;
+
+ // 2. apply priority only zen - verify zen will still be normal
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
+ mZenModeHelperSpy.TAG);
+
+ // 3. change ringer from normal to silent, verify previous ringer set to new rigner (silent)
+ ZenModeHelper.RingerModeDelegate ringerModeDelegate =
+ mZenModeHelperSpy.new RingerModeDelegate();
+ ringerModeDelegate.onSetRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
+ AudioManager.RINGER_MODE_SILENT, "test", AudioManager.RINGER_MODE_NORMAL,
+ VolumePolicy.DEFAULT);
+ assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(),
+ Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL));
+
+ // 4. apply zen off - verify ringer still silenced
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelperSpy.applyZenToRingerMode();
+ verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+ mZenModeHelperSpy.TAG);
+ }
}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index b362df9..26ffe32 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -82,8 +82,7 @@
.noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
return false;
}
- if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
- && !isLegacyForeground(context, pkgName, uid)) {
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
return false;
}
// If the user or profile is current, permission is granted.
@@ -101,35 +100,6 @@
&& locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
}
- private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
- int uid) {
- long token = Binder.clearCallingIdentity();
- try {
- return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
- try {
- if (context.getPackageManager().getApplicationInfo(pkgName, 0)
- .targetSdkVersion <= Build.VERSION_CODES.O) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume known app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking app's version.
- }
- return false;
- }
-
- private static boolean isForegroundApp(@NonNull Context context, int uid) {
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
- }
-
private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
return context.checkCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0ee870a..0446925 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -372,7 +372,7 @@
break;
case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
- (List<PhysicalChannelConfig>)msg.obj);
+ (List<PhysicalChannelConfig>)msg.obj);
break;
}
}
@@ -700,6 +700,10 @@
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
+
+ public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
+ }
}
IPhoneStateListener callback = new IPhoneStateListenerStub(this);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 651d68d..ce444dd 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -27,8 +27,9 @@
*/
public final class PhysicalChannelConfig implements Parcelable {
+ // TODO(b/72993578) consolidate these enums in a central location.
@Retention(RetentionPolicy.SOURCE)
- @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+ @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN})
public @interface ConnectionStatus {}
/**
@@ -41,6 +42,9 @@
*/
public static final int CONNECTION_SECONDARY_SERVING = 2;
+ /** Connection status is unknown. */
+ public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
/**
* Connection status of the cell.
*
@@ -86,6 +90,7 @@
*
* @see #CONNECTION_PRIMARY_SERVING
* @see #CONNECTION_SECONDARY_SERVING
+ * @see #CONNECTION_UNKNOWN
*
* @return Connection status of the cell
*/
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index c3f4007..38f9745 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -116,6 +116,9 @@
/** Result code of an unknown error. */
public static final int RESULT_UNKNOWN_ERROR = -1;
+ /** Result code when the eUICC card with the given card Id is not found. */
+ public static final int RESULT_EUICC_NOT_FOUND = -2;
+
/**
* Callback to receive the result of an eUICC card API.
*
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 8e3f4c0..1cfe8c2 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.VoLteServiceState;
@@ -37,6 +38,7 @@
void onDataConnectionStateChanged(int state, int networkType);
void onDataActivity(int direction);
void onSignalStrengthsChanged(in SignalStrength signalStrength);
+ void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
void onOtaspChanged(in int otaspMode);
void onCellInfoChanged(in List<CellInfo> cellInfo);
void onPreciseCallStateChanged(in PreciseCallState callState);
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 188167c..06dc13e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -21,9 +21,9 @@
import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.telephony.CellInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
-import android.telephony.CellInfo;
import android.telephony.VoLteServiceState;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -58,6 +58,9 @@
void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
void notifyOtaspChanged(in int otaspMode);
void notifyCellInfo(in List<CellInfo> cellInfo);
+ void notifyPhysicalChannelConfiguration(in List<PhysicalChannelConfig> configs);
+ void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+ in List<PhysicalChannelConfig> configs);
void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
int backgroundCallState);
void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
diff --git a/tests/RemoteDisplayProvider/Android.mk b/tests/RemoteDisplayProvider/Android.mk
index 2f4b343..e827ec2 100644
--- a/tests/RemoteDisplayProvider/Android.mk
+++ b/tests/RemoteDisplayProvider/Android.mk
@@ -21,6 +21,6 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
-LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay
+LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay.stubs
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/net/java/android/net/IpPrefixTest.java b/tests/net/java/android/net/IpPrefixTest.java
index b5b2c07..1f1ba2e 100644
--- a/tests/net/java/android/net/IpPrefixTest.java
+++ b/tests/net/java/android/net/IpPrefixTest.java
@@ -223,14 +223,14 @@
}
@Test
- public void testContains() {
+ public void testContainsInetAddress() {
IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
assertTrue(p.contains(Address("2001:db8:f00::ace:d00c")));
assertTrue(p.contains(Address("2001:db8:f00::ace:d00d")));
assertFalse(p.contains(Address("2001:db8:f00::ace:d00e")));
assertFalse(p.contains(Address("2001:db8:f00::bad:d00d")));
assertFalse(p.contains(Address("2001:4868:4860::8888")));
- assertFalse(p.contains(null));
+ assertFalse(p.contains((InetAddress)null));
assertFalse(p.contains(Address("8.8.8.8")));
p = new IpPrefix("192.0.2.0/23");
@@ -251,6 +251,53 @@
}
@Test
+ public void testContainsIpPrefix() {
+ assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0")));
+ assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0")));
+ assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8")));
+ assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24")));
+ assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23")));
+
+ assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8")));
+ assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8")));
+ assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21")));
+ assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32")));
+
+ assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24")));
+
+ assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32")));
+ assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8")));
+ assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15")));
+ assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8")));
+
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113")));
+ assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128")));
+
+ assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:d00d/64")));
+ assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:d00d/120")));
+ assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:d00d/32")));
+ assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
+ new IpPrefix("2006:db8:f00::ace:d00d/96")));
+
+ assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:d00d/128")));
+ assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:ccaf/110")));
+
+ assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
+ new IpPrefix("2001:db8:f00::ace:d00e/128")));
+ assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29")));
+ }
+
+ @Test
public void testHashCode() {
IpPrefix p = new IpPrefix(new byte[4], 0);
Random random = new Random();
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 4227da6..ec6a5ec 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -214,7 +214,9 @@
assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400)));
NetworkCapabilities netCap2 = new NetworkCapabilities();
- assertFalse(netCap2.satisfiedByUids(netCap));
+ // A new netcap object has null UIDs, so anything will satisfy it.
+ assertTrue(netCap2.satisfiedByUids(netCap));
+ // Still not equal though.
assertFalse(netCap2.equalsUids(netCap));
netCap2.setUids(uids);
assertTrue(netCap2.satisfiedByUids(netCap));
@@ -231,7 +233,7 @@
assertTrue(netCap.appliesToUid(650));
assertFalse(netCap.appliesToUid(500));
- assertFalse(new NetworkCapabilities().satisfiedByUids(netCap));
+ assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
netCap.combineCapabilities(new NetworkCapabilities());
assertTrue(netCap.appliesToUid(500));
assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 8d51c3b..a5ee8e3 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -19,8 +19,10 @@
import android.net.NetworkUtils;
import android.test.suitebuilder.annotation.SmallTest;
+import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.util.TreeSet;
import junit.framework.TestCase;
@@ -67,4 +69,101 @@
assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
}
+
+ @SmallTest
+ public void testRoutedIPv4AddressCount() {
+ final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
+ // No routes routes to no addresses.
+ assertEquals(0, NetworkUtils.routedIPv4AddressCount(set));
+
+ set.add(new IpPrefix("0.0.0.0/0"));
+ assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set));
+
+ set.add(new IpPrefix("20.18.0.0/16"));
+ set.add(new IpPrefix("20.18.0.0/24"));
+ set.add(new IpPrefix("20.18.0.0/8"));
+ // There is a default route, still covers everything
+ assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set));
+
+ set.clear();
+ set.add(new IpPrefix("20.18.0.0/24"));
+ set.add(new IpPrefix("20.18.0.0/8"));
+ // The 8-length includes the 24-length prefix
+ assertEquals(1l << 24, NetworkUtils.routedIPv4AddressCount(set));
+
+ set.add(new IpPrefix("10.10.10.126/25"));
+ // The 8-length does not include this 25-length prefix
+ assertEquals((1l << 24) + (1 << 7), NetworkUtils.routedIPv4AddressCount(set));
+
+ set.clear();
+ set.add(new IpPrefix("1.2.3.4/32"));
+ set.add(new IpPrefix("1.2.3.4/32"));
+ set.add(new IpPrefix("1.2.3.4/32"));
+ set.add(new IpPrefix("1.2.3.4/32"));
+ assertEquals(1l, NetworkUtils.routedIPv4AddressCount(set));
+
+ set.add(new IpPrefix("1.2.3.5/32"));
+ set.add(new IpPrefix("1.2.3.6/32"));
+
+ set.add(new IpPrefix("1.2.3.7/32"));
+ set.add(new IpPrefix("1.2.3.8/32"));
+ set.add(new IpPrefix("1.2.3.9/32"));
+ set.add(new IpPrefix("1.2.3.0/32"));
+ assertEquals(7l, NetworkUtils.routedIPv4AddressCount(set));
+
+ // 1.2.3.4/30 eats 1.2.3.{4-7}/32
+ set.add(new IpPrefix("1.2.3.4/30"));
+ set.add(new IpPrefix("6.2.3.4/28"));
+ set.add(new IpPrefix("120.2.3.4/16"));
+ assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set));
+ }
+
+ @SmallTest
+ public void testRoutedIPv6AddressCount() {
+ final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
+ // No routes routes to no addresses.
+ assertEquals(BigInteger.ZERO, NetworkUtils.routedIPv6AddressCount(set));
+
+ set.add(new IpPrefix("::/0"));
+ assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set));
+
+ set.add(new IpPrefix("1234:622a::18/64"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8"));
+ // There is a default route, still covers everything
+ assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set));
+
+ set.clear();
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8"));
+ // The 8-length includes the 96-length prefix
+ assertEquals(BigInteger.ONE.shiftLeft(120), NetworkUtils.routedIPv6AddressCount(set));
+
+ set.add(new IpPrefix("10::26/64"));
+ // The 8-length does not include this 64-length prefix
+ assertEquals(BigInteger.ONE.shiftLeft(120).add(BigInteger.ONE.shiftLeft(64)),
+ NetworkUtils.routedIPv6AddressCount(set));
+
+ set.clear();
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128"));
+ assertEquals(BigInteger.ONE, NetworkUtils.routedIPv6AddressCount(set));
+
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad5/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad6/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad7/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad8/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad9/128"));
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad0/128"));
+ assertEquals(BigInteger.valueOf(7), NetworkUtils.routedIPv6AddressCount(set));
+
+ // add4:f00:80:f7:1111::6ad4/126 eats add4:f00:8[:f7:1111::6ad{4-7}/128
+ set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/126"));
+ set.add(new IpPrefix("d00d:f00:80:f7:1111::6ade/124"));
+ set.add(new IpPrefix("f00b:a33::/112"));
+ assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
+ NetworkUtils.routedIPv6AddressCount(set));
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2aea1d7..207e24a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -387,6 +387,7 @@
mScore = 20;
break;
case TRANSPORT_VPN:
+ mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
break;
default:
@@ -3748,14 +3749,19 @@
final int uid = Process.myUid();
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
- final NetworkRequest genericRequest = new NetworkRequest.Builder().build();
+ final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
final NetworkRequest wifiRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI).build();
final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
.addTransportType(TRANSPORT_VPN).build();
mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+ mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback);
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
@@ -3763,6 +3769,7 @@
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
@@ -3777,16 +3784,19 @@
vpnNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ genericNotVpnNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
ranges.clear();
vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
@@ -3794,18 +3804,21 @@
vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
mWiFiNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ genericNotVpnNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
vpnNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 1dbf9b2..f59850d 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -57,9 +57,13 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnService;
import android.os.Build.VERSION_CODES;
@@ -90,7 +94,8 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Tests for {@link Vpn}.
@@ -563,4 +568,75 @@
return networks.get(network);
}).when(mConnectivityManager).getNetworkCapabilities(any());
}
+
+ // Need multiple copies of this, but Java's Stream objects can't be reused or
+ // duplicated.
+ private Stream<String> publicIpV4Routes() {
+ return Stream.of(
+ "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4",
+ "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6",
+ "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9",
+ "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11",
+ "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14",
+ "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7",
+ "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4");
+ }
+
+ private Stream<String> publicIpV6Routes() {
+ return Stream.of(
+ "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6",
+ "fe00::/8", "2605:ef80:e:af1d::/64");
+ }
+
+ @Test
+ public void testProvidesRoutesToMostDestinations() {
+ final LinkProperties lp = new LinkProperties();
+
+ // Default route provides routes to all IPv4 destinations.
+ lp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0")));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ // Empty LP provides routes to no destination
+ lp.clear();
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ // All IPv4 routes except for local networks. This is the case most relevant
+ // to this function. It provides routes to almost the entire space.
+ // (clone the stream so that we can reuse it later)
+ publicIpV4Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s))));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ // Removing a 16-bit prefix, which is 65536 addresses. This is still enough to
+ // provide routes to "most" destinations.
+ lp.removeRoute(new RouteInfo(new IpPrefix("192.169.0.0/16")));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ // Remove the /2 route, which represent a quarter of the available routing space.
+ // This LP does not provides routes to "most" destinations any more.
+ lp.removeRoute(new RouteInfo(new IpPrefix("64.0.0.0/2")));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.clear();
+ publicIpV6Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s))));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.removeRoute(new RouteInfo(new IpPrefix("::/1")));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ // V6 does not provide sufficient coverage but v4 does
+ publicIpV4Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s))));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ // V4 still does
+ lp.removeRoute(new RouteInfo(new IpPrefix("192.169.0.0/16")));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ // V4 does not any more
+ lp.removeRoute(new RouteInfo(new IpPrefix("64.0.0.0/2")));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ // V4 does not, but V6 has sufficient coverage again
+ lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+ }
}
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 2052f15..eca8406 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -116,9 +116,12 @@
* Called when a discovery (publish or subscribe) operation results in a
* service discovery. Called when a Subscribe service was configured with a range requirement
* {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
- * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
- * (i.e. this callback called) if the range of the publisher is within the specified distance
- * constraints.
+ * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} and the Publish service was configured
+ * with {@link PublishConfig.Builder#setRangingEnabled(boolean)}.
+ * <p>
+ * If either Publisher or Subscriber does not enable Ranging, or if Ranging is temporarily
+ * disabled by the underlying device, service discovery proceeds without ranging and the
+ * {@link #onServiceDiscovered(PeerHandle, byte[], List)} is called.
*
* @param peerHandle An opaque handle to the peer matching our discovery operation.
* @param serviceSpecificInfo The service specific information (arbitrary
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index b525212..8ae4b5a 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -18,11 +18,20 @@
/**
* Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
- * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}, used
- * when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])},
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or
+ * received messages in {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}, and
+ * used when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])},
* or when configuring a network link to a peer, e.g.
* {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or
* {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ * <p>
+ * Note that while a {@code PeerHandle} can be used to track a particular peer (i.e. you can compare
+ * the values received from subsequent messages) - it is good practice not to rely on it. Instead
+ * use an application level peer identifier encoded in the message,
+ * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}, and/or in the Publish
+ * configuration's service-specific information field,
+ * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter,
+ * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}.
*/
public class PeerHandle {
/** @hide */
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index 7a0250b..d43f727 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -365,9 +365,8 @@
* {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
* maximum distance at which discovery will be triggered.
* <p>
- * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
- * device will be refused. If the peer has ranging enabled (using the
- * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+ * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device
+ * will be refused and discovery will proceed without ranging constraints.
* <p>
* The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
* as described in {@link android.net.wifi.rtt}.
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 2eab76a..2ec3b70 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -415,17 +415,23 @@
/**
* Configure the minimum distance to a discovered publisher at which to trigger a discovery
- * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * notification. I.e. discovery will be triggered if we've found a matching publisher
* (based on the other criteria in this configuration) <b>and</b> the distance to the
- * publisher is > the value specified in this API.
+ * publisher is larger than the value specified in this API. Can be used in conjunction with
+ * {@link #setMaxDistanceMm(int)} to specify a geofence, i.e. discovery with min <
+ * distance < max.
* <p>
- * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
- * i.e. discovery with min < distance < max.
+ * For ranging to be used in discovery it must also be enabled on the publisher using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may
+ * not be available or enabled on the publisher or may be temporarily disabled on either
+ * subscriber or publisher - in such cases discovery will proceed without ranging.
* <p>
- * If this API is called, the subscriber requires ranging. In such a case, the publisher
- * peer must enable ranging using
- * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
- * never be triggered.
+ * When ranging is enabled and available on both publisher and subscriber and a service
+ * is discovered based on geofence constraints the
+ * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)}
+ * is called, otherwise the
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}
+ * is called.
* <p>
* The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
* as described in {@link android.net.wifi.rtt}.
@@ -444,17 +450,23 @@
/**
* Configure the maximum distance to a discovered publisher at which to trigger a discovery
- * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * notification. I.e. discovery will be triggered if we've found a matching publisher
* (based on the other criteria in this configuration) <b>and</b> the distance to the
- * publisher is < the value specified in this API.
+ * publisher is smaller than the value specified in this API. Can be used in conjunction
+ * with {@link #setMinDistanceMm(int)} to specify a geofence, i.e. discovery with min <
+ * distance < max.
* <p>
- * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
- * i.e. discovery with min < distance < max.
+ * For ranging to be used in discovery it must also be enabled on the publisher using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may
+ * not be available or enabled on the publisher or may be temporarily disabled on either
+ * subscriber or publisher - in such cases discovery will proceed without ranging.
* <p>
- * If this API is called, the subscriber requires ranging. In such a case, the publisher
- * peer must enable ranging using
- * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
- * never be triggered.
+ * When ranging is enabled and available on both publisher and subscriber and a service
+ * is discovered based on geofence constraints the
+ * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)}
+ * is called, otherwise the
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}
+ * is called.
* <p>
* The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
* as described in {@link android.net.wifi.rtt}.
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 06a5c2e..8529a89 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -27,6 +27,7 @@
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -420,9 +421,12 @@
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (peerHandle == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer handle - cannot be null");
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+ Build.VERSION_CODES.P)) {
+ if (peerHandle == null) {
+ throw new IllegalArgumentException(
+ "createNetworkSpecifier: Invalid peer handle - cannot be null");
+ }
}
return new WifiAwareNetworkSpecifier(
@@ -453,9 +457,12 @@
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (peer == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer MAC - cannot be null");
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+ Build.VERSION_CODES.P)) {
+ if (peer == null) {
+ throw new IllegalArgumentException(
+ "createNetworkSpecifier: Invalid peer MAC - cannot be null");
+ }
}
if (peer != null && peer.length != 6) {
throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
index fda7a9a..3ece93d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
@@ -16,6 +16,8 @@
package android.net.wifi.aware;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.Constants;
/**
@@ -84,4 +86,21 @@
return true;
}
+
+ /**
+ * Returns true if the App version is older than minVersion.
+ */
+ public static boolean isLegacyVersion(Context context, int minVersion) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(context.getOpPackageName(), 0)
+ .targetSdkVersion < minVersion) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify valididity before checking App's version.
+ }
+ return false;
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 84e3ed9..272f727 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,14 +19,20 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.wifi.RttManager;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
@@ -79,12 +85,32 @@
@Mock
public RttManager.RttListener mockRttListener;
+ @Mock
+ public PackageManager mockPackageManager;
+
+ @Mock
+ public ApplicationInfo mockApplicationInfo;
+
private static final int AWARE_STATUS_ERROR = -1;
+ private static final byte[] PMK_VALID = "01234567890123456789012345678901".getBytes();
+ private static final byte[] PMK_INVALID = "012".getBytes();
+
+ private static final String PASSPHRASE_VALID = "SomeLongEnoughPassphrase";
+ private static final String PASSPHRASE_TOO_SHORT = "012";
+ private static final String PASSPHRASE_TOO_LONG =
+ "0123456789012345678901234567890123456789012345678901234567890123456789";
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+ when(mockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ mockApplicationInfo);
+ when(mockContext.getOpPackageName()).thenReturn("XXX");
+ when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
+
mDut = new WifiAwareManager(mockContext, mockAwareService);
mMockLooper = new TestLooper();
mMockLooperHandler = new Handler(mMockLooper.getLooper());
@@ -884,8 +910,8 @@
final int sessionId = 123;
final PeerHandle peerHandle = new PeerHandle(123412);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
- final byte[] pmk = "01234567890123456789012345678901".getBytes();
- final String passphrase = "A really bad password";
+ final byte[] pmk = PMK_VALID;
+ final String passphrase = PASSPHRASE_VALID;
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -965,8 +991,8 @@
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
- final byte[] pmk = "01234567890123456789012345678901".getBytes();
- final String passphrase = "A really bad password";
+ final byte[] pmk = PMK_VALID;
+ final String passphrase = PASSPHRASE_VALID;
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
@@ -1030,7 +1056,7 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierWithClientIncorrectLengthPmk() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), true, "012".getBytes(), null);
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), true, PMK_INVALID, null);
}
/**
@@ -1045,17 +1071,17 @@
* Validate that a too short Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierWithClientShortPassphrase() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, "012");
+ public void testNetworkSpecifierWithClientTooShortPassphrase() throws Exception {
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
+ PASSPHRASE_TOO_SHORT);
}
/**
* Validate that a too long Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierWithClientLongPassphrase() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ public void testNetworkSpecifierWithClientTooLongPassphrase() throws Exception {
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, PASSPHRASE_TOO_LONG);
}
/**
@@ -1063,8 +1089,16 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierWithClientNullPeer() throws Exception {
- executeNetworkSpecifierWithClient(null, false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
+ }
+
+ /**
+ * Validate that a null PeerHandle does not trigger an exception for legacy API.
+ */
+ @Test
+ public void testNetworkSpecifierWithClientNullPeerLegacyApi() throws Exception {
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+ executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
}
private void executeNetworkSpecifierWithClient(PeerHandle peerHandle, boolean doPmk, byte[] pmk,
@@ -1117,7 +1151,7 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectNullPmk() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
- null, null);
+ null, null, true);
}
/**
@@ -1126,7 +1160,7 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectIncorrectLengthPmk() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
- "012".getBytes(), null);
+ PMK_INVALID, null, true);
}
/**
@@ -1135,40 +1169,57 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectNullPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null, null);
+ false, null, null, true);
}
/**
* Validate that a too short Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectShortPassphrase() throws Exception {
+ public void testNetworkSpecifierDirectTooShortPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null, "012");
+ false, null, PASSPHRASE_TOO_SHORT, true);
}
/**
* Validate that a too long Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectLongPassphrase() throws Exception {
+ public void testNetworkSpecifierDirectTooLongPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ false, null, PASSPHRASE_TOO_LONG, true);
}
/**
- * Validate that a null peer MAC triggers an exception.
+ * Validate that a null peer MAC triggers an exception for an Initiator.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectNullPeer() throws Exception {
- executeNetworkSpecifierDirect(null, false, null, null);
+ public void testNetworkSpecifierDirectNullPeerInitiator() throws Exception {
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, true);
+ }
+
+ /**
+ * Validate that a null peer MAC triggers an exception for a Resonder.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testNetworkSpecifierDirectNullPeerResponder() throws Exception {
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
+ }
+
+ /**
+ * Validate that a null peer MAC does not trigger an exception for a Resonder on legacy API.
+ */
+ @Test
+ public void testNetworkSpecifierDirectNullPeerResponderLegacyApi() throws Exception {
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
}
private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk,
- String passphrase) throws Exception {
+ String passphrase, boolean doInitiator) throws Exception {
final int clientId = 134;
- final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+ final int role = doInitiator ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(