Merge "Fix statsd dropping metrics data." into pi-dev
diff --git a/api/current.txt b/api/current.txt
index 6ea38cc..dc3ab4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16449,8 +16449,8 @@
}
public final class SessionConfiguration {
- ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler);
- method public android.os.Handler getHandler();
+ ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
+ method public java.util.concurrent.Executor getExecutor();
method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
method public android.hardware.camera2.CaptureRequest getSessionParameters();
@@ -21545,10 +21545,10 @@
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
method public double getAutomaticGainControlLevelDb();
- method public long getCarrierCycles();
+ method public deprecated long getCarrierCycles();
method public float getCarrierFrequencyHz();
- method public double getCarrierPhase();
- method public double getCarrierPhaseUncertainty();
+ method public deprecated double getCarrierPhase();
+ method public deprecated double getCarrierPhaseUncertainty();
method public double getCn0DbHz();
method public int getConstellationType();
method public int getMultipathIndicator();
@@ -21561,13 +21561,15 @@
method public int getSvid();
method public double getTimeOffsetNanos();
method public boolean hasAutomaticGainControlLevelDb();
- method public boolean hasCarrierCycles();
+ method public deprecated boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
- method public boolean hasCarrierPhase();
- method public boolean hasCarrierPhaseUncertainty();
+ method public deprecated boolean hasCarrierPhase();
+ method public deprecated boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
+ field public static final int ADR_STATE_HALF_CYCLE_REPORTED = 16; // 0x10
+ field public static final int ADR_STATE_HALF_CYCLE_RESOLVED = 8; // 0x8
field public static final int ADR_STATE_RESET = 2; // 0x2
field public static final int ADR_STATE_UNKNOWN = 0; // 0x0
field public static final int ADR_STATE_VALID = 1; // 0x1
@@ -22089,7 +22091,6 @@
method public boolean isBluetoothScoOn();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
- method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
@@ -22415,7 +22416,6 @@
method public int reloadStaticData();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
- method public void removeStreamEventCallback();
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -22430,7 +22430,6 @@
method public int setPresentation(android.media.AudioPresentation);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
- method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
method public int setVolume(float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -22466,7 +22465,6 @@
method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
- method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method public android.media.AudioTrack.Builder setPerformanceMode(int);
method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22490,12 +22488,6 @@
method public default void onRoutingChanged(android.media.AudioRouting);
}
- public static abstract class AudioTrack.StreamEventCallback {
- method public void onStreamDataRequest(android.media.AudioTrack);
- method public void onStreamPresentationEnd(android.media.AudioTrack);
- method public void onTearDown(android.media.AudioTrack);
- }
-
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -22548,40 +22540,6 @@
field public static final int QUALITY_MEDIUM = 1; // 0x1
}
- public final class DataSourceDesc {
- method public long getEndPosition();
- method public java.io.FileDescriptor getFileDescriptor();
- method public long getFileDescriptorLength();
- method public long getFileDescriptorOffset();
- 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();
- method public android.content.Context getUriContext();
- method public java.util.List<java.net.HttpCookie> getUriCookies();
- method public java.util.Map<java.lang.String, java.lang.String> getUriHeaders();
- field public static final long LONG_MAX = 576460752303423487L; // 0x7ffffffffffffffL
- field public static final int TYPE_CALLBACK = 1; // 0x1
- field public static final int TYPE_FD = 2; // 0x2
- field public static final int TYPE_NONE = 0; // 0x0
- field public static final int TYPE_URI = 3; // 0x3
- }
-
- public static class DataSourceDesc.Builder {
- ctor public DataSourceDesc.Builder();
- ctor public DataSourceDesc.Builder(android.media.DataSourceDesc);
- method public android.media.DataSourceDesc build();
- method public android.media.DataSourceDesc.Builder setDataSource(android.media.Media2DataSource);
- method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor);
- method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor, long, long);
- 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 setMediaId(java.lang.String);
- method public android.media.DataSourceDesc.Builder setStartPosition(long);
- }
-
public final class DeniedByServerException extends android.media.MediaDrmException {
ctor public DeniedByServerException(java.lang.String);
}
@@ -22859,12 +22817,6 @@
method public abstract void onJetUserIdUpdate(android.media.JetPlayer, int, int);
}
- public abstract class Media2DataSource implements java.io.Closeable {
- ctor public Media2DataSource();
- method public abstract long getSize() throws java.io.IOException;
- method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
- }
-
public class MediaActionSound {
ctor public MediaActionSound();
method public void load(int);
@@ -22876,27 +22828,6 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
- public class MediaBrowser2 extends android.media.MediaController2 {
- ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback);
- method public void getChildren(java.lang.String, int, int, android.os.Bundle);
- method public void getItem(java.lang.String);
- method public void getLibraryRoot(android.os.Bundle);
- method public void getSearchResult(java.lang.String, int, int, android.os.Bundle);
- method public void search(java.lang.String, android.os.Bundle);
- method public void subscribe(java.lang.String, android.os.Bundle);
- method public void unsubscribe(java.lang.String);
- }
-
- public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback {
- ctor public MediaBrowser2.BrowserCallback();
- method public void onChildrenChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
- method public void onGetChildrenDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
- method public void onGetItemDone(android.media.MediaBrowser2, java.lang.String, android.media.MediaItem2);
- method public void onGetLibraryRootDone(android.media.MediaBrowser2, android.os.Bundle, java.lang.String, android.os.Bundle);
- method public void onGetSearchResultDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
- method public void onSearchResultChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
- }
-
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
@@ -23374,82 +23305,6 @@
field public static final int REGULAR_CODECS = 0; // 0x0
}
- public class MediaController2 implements java.lang.AutoCloseable {
- ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public void adjustVolume(int, int);
- method public void close();
- method public void fastForward();
- method public long getBufferedPosition();
- method public android.media.MediaItem2 getCurrentMediaItem();
- method public android.media.MediaController2.PlaybackInfo getPlaybackInfo();
- method public float getPlaybackSpeed();
- method public int getPlayerState();
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public long getPosition();
- method public int getRepeatMode();
- method public android.app.PendingIntent getSessionActivity();
- method public android.media.SessionToken2 getSessionToken();
- method public int getShuffleMode();
- method public boolean isConnected();
- method public void pause();
- method public void play();
- method public void playFromMediaId(java.lang.String, android.os.Bundle);
- method public void playFromSearch(java.lang.String, android.os.Bundle);
- method public void playFromUri(android.net.Uri, android.os.Bundle);
- method public void prepare();
- method public void prepareFromMediaId(java.lang.String, android.os.Bundle);
- method public void prepareFromSearch(java.lang.String, android.os.Bundle);
- method public void prepareFromUri(android.net.Uri, android.os.Bundle);
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void rewind();
- method public void seekTo(long);
- method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void setPlaybackSpeed(float);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRating(java.lang.String, android.media.Rating2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void setVolumeTo(int, int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public void stop();
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- }
-
- public static abstract class MediaController2.ControllerCallback {
- ctor public MediaController2.ControllerCallback();
- method public void onAllowedCommandsChanged(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
- method public void onBufferedPositionChanged(android.media.MediaController2, long);
- method public void onConnected(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
- method public void onCurrentMediaItemChanged(android.media.MediaController2, android.media.MediaItem2);
- method public void onCustomCommand(android.media.MediaController2, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void onCustomLayoutChanged(android.media.MediaController2, java.util.List<android.media.MediaSession2.CommandButton>);
- method public void onDisconnected(android.media.MediaController2);
- method public void onError(android.media.MediaController2, int, android.os.Bundle);
- method public void onPlaybackInfoChanged(android.media.MediaController2, android.media.MediaController2.PlaybackInfo);
- method public void onPlaybackSpeedChanged(android.media.MediaController2, float);
- method public void onPlayerStateChanged(android.media.MediaController2, int);
- method public void onPlaylistChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onPositionChanged(android.media.MediaController2, long, long);
- method public void onRepeatModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
- method public void onShuffleModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
- }
-
- public static final class MediaController2.PlaybackInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public int getControlType();
- method public int getCurrentVolume();
- method public int getMaxVolume();
- method public int getPlaybackType();
- field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
- field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
- }
-
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
@@ -23850,71 +23705,6 @@
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
- public class MediaItem2 {
- method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle);
- method public android.media.DataSourceDesc getDataSourceDesc();
- method public int getFlags();
- method public java.lang.String getMediaId();
- method public android.media.MediaMetadata2 getMetadata();
- method public boolean isBrowsable();
- method public boolean isPlayable();
- method public void setMetadata(android.media.MediaMetadata2);
- method public android.os.Bundle toBundle();
- field public static final int FLAG_BROWSABLE = 1; // 0x1
- field public static final int FLAG_PLAYABLE = 2; // 0x2
- }
-
- public static final class MediaItem2.Builder {
- ctor public MediaItem2.Builder(android.content.Context, int);
- method public android.media.MediaItem2 build();
- method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc);
- method public android.media.MediaItem2.Builder setMediaId(java.lang.String);
- method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2);
- }
-
- public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 {
- ctor public MediaLibraryService2();
- method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
- }
-
- public static final class MediaLibraryService2.LibraryRoot {
- ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle);
- method public android.os.Bundle getExtras();
- method public java.lang.String getRootId();
- field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
- field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT";
- field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
- }
-
- public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 {
- method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
- method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle);
- method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
- }
-
- public static final class MediaLibraryService2.MediaLibrarySession.Builder {
- ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, 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 setPlaylistAgent(android.media.MediaPlaylistAgent);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
- }
-
- public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback {
- ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context);
- method public java.util.List<android.media.MediaItem2> onGetChildren(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
- method public android.media.MediaItem2 onGetItem(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
- method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, android.os.Bundle);
- method public java.util.List<android.media.MediaItem2> onGetSearchResult(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
- method public void onSearch(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onSubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onUnsubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
- }
-
public final class MediaMetadata implements android.os.Parcelable {
method public boolean containsKey(java.lang.String);
method public int describeContents();
@@ -23970,79 +23760,6 @@
method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
}
- public final class MediaMetadata2 {
- method public boolean containsKey(java.lang.String);
- method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle);
- method public android.graphics.Bitmap getBitmap(java.lang.String);
- method public android.os.Bundle getExtras();
- method public float getFloat(java.lang.String);
- method public long getLong(java.lang.String);
- method public java.lang.String getMediaId();
- method public android.media.Rating2 getRating(java.lang.String);
- method public java.lang.String getString(java.lang.String);
- method public java.lang.CharSequence getText(java.lang.String);
- method public java.util.Set<java.lang.String> keySet();
- method public int size();
- method public android.os.Bundle toBundle();
- field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
- field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
- field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
- field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
- field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
- field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
- field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
- field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
- field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
- field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
- field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
- field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
- field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
- field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
- field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
- field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
- field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
- field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
- field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
- field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
- field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
- field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
- field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
- field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
- field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
- field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
- field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
- field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
- field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
- field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
- field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
- field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
- field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
- field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN";
- field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY";
- field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
- field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
- field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
- field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
- field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
- field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
- field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
- field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
- field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
- }
-
- public static final class MediaMetadata2.Builder {
- ctor public MediaMetadata2.Builder(android.content.Context);
- ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2);
- method public android.media.MediaMetadata2 build();
- method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
- method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float);
- method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long);
- method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2);
- method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String);
- method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence);
- method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle);
- }
-
public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
@@ -24361,263 +24078,6 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
- 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 int getAudioSessionId();
- 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.os.PersistableBundle getMetrics();
- method public abstract android.media.PlaybackParams getPlaybackParams();
- 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 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[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void releaseDrm() 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 setAudioSessionId(int);
- method public abstract void setAuxEffectSendLevel(float);
- 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 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 setSurface(android.view.Surface);
- method public abstract void setSyncParams(android.media.SyncParams);
- field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
- field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
- field public static final int CALL_COMPLETED_LOOP_CURRENT = 3; // 0x3
- field public static final int CALL_COMPLETED_PAUSE = 4; // 0x4
- field public static final int CALL_COMPLETED_PLAY = 5; // 0x5
- field public static final int CALL_COMPLETED_PREPARE = 6; // 0x6
- field public static final int CALL_COMPLETED_RELEASE_DRM = 12; // 0xc
- field public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13; // 0xd
- field public static final int CALL_COMPLETED_SEEK_TO = 14; // 0xe
- field public static final int CALL_COMPLETED_SELECT_TRACK = 15; // 0xf
- field public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16; // 0x10
- field public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17; // 0x11
- field public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
- field public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19; // 0x13
- field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22; // 0x16
- field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23; // 0x17
- field public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24; // 0x18
- field public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25; // 0x19
- field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
- field public static final int CALL_COMPLETED_SET_SURFACE = 27; // 0x1b
- field public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28; // 0x1c
- field public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29; // 0x1d
- field public static final int CALL_STATUS_BAD_VALUE = 2; // 0x2
- field public static final int CALL_STATUS_ERROR_IO = 4; // 0x4
- field public static final int CALL_STATUS_ERROR_UNKNOWN = -2147483648; // 0x80000000
- field public static final int CALL_STATUS_INVALID_OPERATION = 1; // 0x1
- field public static final int CALL_STATUS_NO_DRM_SCHEME = 5; // 0x5
- field public static final int CALL_STATUS_NO_ERROR = 0; // 0x0
- field public static final int CALL_STATUS_PERMISSION_DENIED = 3; // 0x3
- 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
- field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
- field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
- field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
- field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
- field public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4; // 0x4
- 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_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
- field public static final int MEDIA_INFO_PLAYLIST_END = 6; // 0x6
- field public static final int MEDIA_INFO_PREPARED = 100; // 0x64
- field public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; // 0x2
- field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
- field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
- field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
- field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
- field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
- field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
- field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
- field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
- field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
- field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
- field public static final int SEEK_CLOSEST = 3; // 0x3
- field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
- field public static final int SEEK_NEXT_SYNC = 1; // 0x1
- field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
- field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
- }
-
- public static abstract class MediaPlayer2.DrmEventCallback {
- ctor public MediaPlayer2.DrmEventCallback();
- 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 {
- ctor public MediaPlayer2.DrmInfo();
- method public abstract java.util.Map<java.util.UUID, byte[]> getPssh();
- method public abstract java.util.List<java.util.UUID> getSupportedSchemes();
- }
-
- public static abstract class MediaPlayer2.MediaPlayer2EventCallback {
- ctor public MediaPlayer2.MediaPlayer2EventCallback();
- method public void onCallCompleted(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 {
- field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
- field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
- field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
- field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
- field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
- field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
- field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
- field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
- field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
- field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
- field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
- field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
- }
-
- public static abstract class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.NoDrmSchemeException(java.lang.String);
- }
-
- public static abstract interface MediaPlayer2.OnDrmConfigHelper {
- method public abstract void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc);
- }
-
- public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.ProvisioningNetworkErrorException(java.lang.String);
- }
-
- public static abstract class MediaPlayer2.ProvisioningServerErrorException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.ProvisioningServerErrorException(java.lang.String);
- }
-
- public static abstract class MediaPlayer2.TrackInfo {
- ctor public MediaPlayer2.TrackInfo();
- method public abstract android.media.MediaFormat getFormat();
- method public abstract java.lang.String getLanguage();
- method public abstract int getTrackType();
- method public abstract java.lang.String toString();
- field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
- field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
- field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
- field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
- field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
- }
-
- public abstract class MediaPlayerBase implements java.lang.AutoCloseable {
- ctor public MediaPlayerBase();
- method public abstract android.media.AudioAttributes getAudioAttributes();
- method public long getBufferedPosition();
- method public abstract int getBufferingState();
- method public abstract android.media.DataSourceDesc getCurrentDataSource();
- method public long getCurrentPosition();
- method public long getDuration();
- method public float getMaxPlayerVolume();
- method public float getPlaybackSpeed();
- method public abstract int getPlayerState();
- method public abstract float getPlayerVolume();
- method public boolean isReversePlaybackSupported();
- method public abstract void loopCurrent(boolean);
- method public abstract void pause();
- method public abstract void play();
- method public abstract void prepare();
- method public abstract void registerPlayerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayerBase.PlayerEventCallback);
- method public abstract void reset();
- method public abstract void seekTo(long);
- method public abstract void setAudioAttributes(android.media.AudioAttributes);
- method public abstract void setDataSource(android.media.DataSourceDesc);
- 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 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
- field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
- field public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3; // 0x3
- field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
- field public static final int PLAYER_STATE_ERROR = 3; // 0x3
- field public static final int PLAYER_STATE_IDLE = 0; // 0x0
- field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
- field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
- field public static final long UNKNOWN_TIME = -1L; // 0xffffffffffffffffL
- }
-
- public static abstract class MediaPlayerBase.PlayerEventCallback {
- ctor public MediaPlayerBase.PlayerEventCallback();
- method public void onBufferingStateChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc, int);
- method public void onCurrentDataSourceChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc);
- method public void onMediaPrepared(android.media.MediaPlayerBase, android.media.DataSourceDesc);
- method public void onPlayerStateChanged(android.media.MediaPlayerBase, int);
- }
-
- public abstract class MediaPlaylistAgent {
- ctor public MediaPlaylistAgent(android.content.Context);
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public int getRepeatMode();
- method public int getShuffleMode();
- method public final void notifyPlaylistChanged();
- method public final void notifyPlaylistMetadataChanged();
- method public final void notifyRepeatModeChanged();
- method public final void notifyShuffleModeChanged();
- method public final void registerPlaylistEventCallback(java.util.concurrent.Executor, android.media.MediaPlaylistAgent.PlaylistEventCallback);
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public final void unregisterPlaylistEventCallback(android.media.MediaPlaylistAgent.PlaylistEventCallback);
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- field public static final int REPEAT_MODE_ALL = 2; // 0x2
- field public static final int REPEAT_MODE_GROUP = 3; // 0x3
- field public static final int REPEAT_MODE_NONE = 0; // 0x0
- field public static final int REPEAT_MODE_ONE = 1; // 0x1
- field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
- field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
- field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
- }
-
- public static abstract class MediaPlaylistAgent.PlaylistEventCallback {
- ctor public MediaPlaylistAgent.PlaylistEventCallback();
- method public void onPlaylistChanged(android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onRepeatModeChanged(android.media.MediaPlaylistAgent, int);
- method public void onShuffleModeChanged(android.media.MediaPlaylistAgent, int);
- }
-
public class MediaRecorder implements android.media.AudioRouting {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -24894,187 +24354,6 @@
method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
}
- public class MediaSession2 implements java.lang.AutoCloseable {
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public void clearOnDataSourceMissingHelper();
- method public void close();
- method public void fastForward();
- method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
- method public android.media.MediaItem2 getCurrentMediaItem();
- method public float getPlaybackSpeed();
- method public android.media.MediaPlayerBase getPlayer();
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaPlaylistAgent getPlaylistAgent();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public int getRepeatMode();
- method public int getShuffleMode();
- method public android.media.SessionToken2 getToken();
- 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();
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void rewind();
- method public void seekTo(long);
- method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
- method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
- method public void setAudioFocusRequest(android.media.AudioFocusRequest);
- method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
- method public void setOnDataSourceMissingHelper(android.media.MediaSession2.OnDataSourceMissingHelper);
- method public void setPlaybackSpeed(float);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public void stop();
- method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistAgent, android.media.VolumeProvider2);
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- field public static final int COMMAND_CODE_BROWSER = 28; // 0x1c
- field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
- field public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11; // 0xb
- field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
- field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2
- field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1
- field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
- field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
- field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
- field public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10; // 0xa
- field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
- field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
- field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
- field public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15; // 0xf
- field public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18; // 0x12
- field public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20; // 0x14
- field public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16; // 0x10
- field public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17; // 0x11
- field public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19; // 0x13
- field public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21; // 0x15
- field public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14; // 0xe
- field public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13; // 0xd
- field public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12; // 0xc
- field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 22; // 0x16
- field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 24; // 0x18
- field public static final int COMMAND_CODE_PLAY_FROM_URI = 23; // 0x17
- field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 25; // 0x19
- field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 27; // 0x1b
- field public static final int COMMAND_CODE_PREPARE_FROM_URI = 26; // 0x1a
- field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
- field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
- field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
- field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
- field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
- field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
- field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
- field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
- field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
- field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
- field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc
- field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
- field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
- }
-
- public static final class MediaSession2.Builder {
- ctor public MediaSession2.Builder(android.content.Context);
- 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 setPlaylistAgent(android.media.MediaPlaylistAgent);
- method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
- method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
- method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
- }
-
- public static final class MediaSession2.Command {
- ctor public MediaSession2.Command(android.content.Context, int);
- ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
- method public int getCommandCode();
- method public java.lang.String getCustomCommand();
- method public android.os.Bundle 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 getExtras();
- method public int getIconResId();
- method public boolean isEnabled();
- }
-
- public static final class MediaSession2.CommandButton.Builder {
- ctor public MediaSession2.CommandButton.Builder(android.content.Context);
- method public android.media.MediaSession2.CommandButton build();
- method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
- method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
- method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
- method public android.media.MediaSession2.CommandButton.Builder setExtras(android.os.Bundle);
- method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
- }
-
- public static final class MediaSession2.CommandGroup {
- ctor public MediaSession2.CommandGroup(android.content.Context);
- ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
- method public void addAllPredefinedCommands();
- method public void addCommand(android.media.MediaSession2.Command);
- method public 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);
- }
-
- public static final class MediaSession2.ControllerInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- method public boolean isTrusted();
- }
-
- public static abstract interface MediaSession2.OnDataSourceMissingHelper {
- method public abstract android.media.DataSourceDesc onDataSourceMissing(android.media.MediaSession2, android.media.MediaItem2);
- }
-
- public static abstract class MediaSession2.SessionCallback {
- ctor public MediaSession2.SessionCallback(android.content.Context);
- method public void onBufferingStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2, int);
- method public boolean onCommandRequest(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
- method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
- method public void onCurrentMediaItemChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
- method public void onCustomCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
- method public void onMediaPrepared(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
- method public void onPlayFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPlayFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPlayFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
- method public void onPlayerStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, int);
- method public void onPlaylistChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onPrepareFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPrepareFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPrepareFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
- method public void onRepeatModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
- method public void onSetRating(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
- method public void onShuffleModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
- }
-
- public abstract class MediaSessionService2 extends android.app.Service {
- ctor public MediaSessionService2();
- method public final android.media.MediaSession2 getSession();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.MediaSession2 onCreateSession(java.lang.String);
- method public android.media.MediaSessionService2.MediaNotification onUpdateNotification();
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2";
- field public static final java.lang.String SERVICE_META_DATA = "android.media.session";
- }
-
- public static class MediaSessionService2.MediaNotification {
- ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification);
- method public android.app.Notification getNotification();
- method public int getNotificationId();
- }
-
public final class MediaSync {
ctor public MediaSync();
method public android.view.Surface createInputSurface();
@@ -25367,22 +24646,6 @@
field public static final int URI_COLUMN_INDEX = 2; // 0x2
}
- public final class SessionToken2 {
- ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String);
- method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle);
- method public java.lang.String getId();
- method public java.lang.String getPackageName();
- method public int getType();
- method public int getUid();
- method public android.os.Bundle toBundle();
- field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
- field public static final int TYPE_SESSION = 0; // 0x0
- field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
- }
-
- public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation {
- }
-
public class SoundPool {
ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
@@ -25586,19 +24849,6 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
- public abstract class VolumeProvider2 {
- ctor public VolumeProvider2(android.content.Context, int, int, int);
- method public final int getControlType();
- method public final int getCurrentVolume();
- method public final int getMaxVolume();
- method public void onAdjustVolume(int);
- method public void onSetVolumeTo(int);
- method public final void setCurrentVolume(int);
- field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
- field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
- field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
- }
-
public final class VolumeShaper implements java.lang.AutoCloseable {
method public void apply(android.media.VolumeShaper.Operation);
method public void close();
@@ -26541,23 +25791,14 @@
public final class MediaSessionManager {
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
- method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
- method public java.util.List<android.media.SessionToken2> getActiveSessionTokens();
method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
- method public java.util.List<android.media.SessionToken2> getAllSessionTokens();
- method public java.util.List<android.media.SessionToken2> getSessionServiceTokens();
method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
- method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
}
public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
}
- public static abstract interface MediaSessionManager.OnSessionTokensChangedListener {
- method public abstract void onSessionTokensChanged(java.util.List<android.media.SessionToken2>);
- }
-
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
@@ -44056,7 +43297,7 @@
method public abstract int getSpanTypeId();
}
- public class PrecomputedText implements android.text.Spanned {
+ public class PrecomputedText implements android.text.Spannable {
method public char charAt(int);
method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
method public int getParagraphCount();
@@ -44070,6 +43311,8 @@
method public java.lang.CharSequence getText();
method public int length();
method public int nextSpanTransition(int, int, java.lang.Class);
+ method public void removeSpan(java.lang.Object);
+ method public void setSpan(java.lang.Object, int, int, int);
method public java.lang.CharSequence subSequence(int, int);
}
@@ -53432,20 +52675,6 @@
method public void update();
}
- public class MediaControlView2 extends android.view.ViewGroup {
- ctor public MediaControlView2(android.content.Context);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int, int);
- method public void requestPlayButtonFocus();
- method public void setMediaSessionToken(android.media.SessionToken2);
- method public void setOnFullScreenListener(android.widget.MediaControlView2.OnFullScreenListener);
- }
-
- public static abstract interface MediaControlView2.OnFullScreenListener {
- method public abstract void onFullScreen(android.view.View, boolean);
- }
-
public class MediaController extends android.widget.FrameLayout {
ctor public MediaController(android.content.Context, android.util.AttributeSet);
ctor public MediaController(android.content.Context, boolean);
@@ -54836,27 +54065,6 @@
method public void suspend();
}
- public class VideoView2 extends android.view.ViewGroup {
- ctor public VideoView2(android.content.Context);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet, int);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet, int, int);
- method public android.widget.MediaControlView2 getMediaControlView2();
- method public android.media.SessionToken2 getMediaSessionToken();
- method public int getViewType();
- method public boolean isSubtitleEnabled();
- method public void setAudioAttributes(android.media.AudioAttributes);
- method public void setAudioFocusRequest(int);
- method public void setDataSource(android.media.DataSourceDesc);
- method public void setMediaControlView2(android.widget.MediaControlView2, long);
- method public void setMediaItem(android.media.MediaItem2);
- method public void setSpeed(float);
- method public void setSubtitleEnabled(boolean);
- method public void setViewType(int);
- field public static final int VIEW_TYPE_SURFACEVIEW = 1; // 0x1
- field public static final int VIEW_TYPE_TEXTUREVIEW = 2; // 0x2
- }
-
public class ViewAnimator extends android.widget.FrameLayout {
ctor public ViewAnimator(android.content.Context);
ctor public ViewAnimator(android.content.Context, android.util.AttributeSet);
diff --git a/api/system-current.txt b/api/system-current.txt
index 137c3db..d602b56 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4318,7 +4318,6 @@
method public int getUserSecretType();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainProtectionParams> CREATOR;
- field public static final int TYPE_CUSTOM_PASSWORD = 101; // 0x65
field public static final int TYPE_LOCKSCREEN = 100; // 0x64
field public static final int UI_FORMAT_PASSWORD = 2; // 0x2
field public static final int UI_FORMAT_PATTERN = 3; // 0x3
@@ -4350,11 +4349,14 @@
}
public final class KeyDerivationParams implements android.os.Parcelable {
+ method public static android.security.keystore.recovery.KeyDerivationParams createScryptParams(byte[], int);
method public static android.security.keystore.recovery.KeyDerivationParams createSha256Params(byte[]);
method public int describeContents();
method public int getAlgorithm();
+ method public int getMemoryDifficulty();
method public byte[] getSalt();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ALGORITHM_SCRYPT = 2; // 0x2
field public static final int ALGORITHM_SHA256 = 1; // 0x1
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyDerivationParams> CREATOR;
}
@@ -4373,7 +4375,6 @@
method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
method public android.security.keystore.recovery.KeyChainSnapshot getKeyChainSnapshot() throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
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;
@@ -4382,7 +4383,6 @@
method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
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;
method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 81cebf0..dd16771 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -416,20 +416,20 @@
ctor public GnssMeasurement();
method public void reset();
method public void resetAutomaticGainControlLevel();
- method public void resetCarrierCycles();
+ method public deprecated void resetCarrierCycles();
method public void resetCarrierFrequencyHz();
- method public void resetCarrierPhase();
- method public void resetCarrierPhaseUncertainty();
+ method public deprecated void resetCarrierPhase();
+ method public deprecated void resetCarrierPhaseUncertainty();
method public void resetSnrInDb();
method public void set(android.location.GnssMeasurement);
method public void setAccumulatedDeltaRangeMeters(double);
method public void setAccumulatedDeltaRangeState(int);
method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
method public void setAutomaticGainControlLevelInDb(double);
- method public void setCarrierCycles(long);
+ method public deprecated void setCarrierCycles(long);
method public void setCarrierFrequencyHz(float);
- method public void setCarrierPhase(double);
- method public void setCarrierPhaseUncertainty(double);
+ method public deprecated void setCarrierPhase(double);
+ method public deprecated void setCarrierPhaseUncertainty(double);
method public void setCn0DbHz(double);
method public void setConstellationType(int);
method public void setMultipathIndicator(int);
@@ -441,6 +441,7 @@
method public void setState(int);
method public void setSvid(int);
method public void setTimeOffsetNanos(double);
+ field public static final int ADR_STATE_ALL = 31; // 0x1f
}
public final class GnssMeasurementsEvent implements android.os.Parcelable {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3d8aa47..643d2bf 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -167,8 +167,7 @@
return;
}
- const char* suffix =
- StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+ string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
dirent* de;
while ((de = readdir(dir.get()))) {
@@ -176,9 +175,9 @@
if (name[0] == '.') continue;
size_t nameLen = strlen(name);
- size_t suffixLen = strlen(suffix);
+ size_t suffixLen = suffix.length();
if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+ strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
int64_t result[3];
parseFileName(name, result);
if (result[0] == -1) continue;
@@ -262,8 +261,7 @@
return false;
}
- const char* suffix =
- StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+ string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
dirent* de;
while ((de = readdir(dir.get()))) {
@@ -272,10 +270,10 @@
continue;
}
size_t nameLen = strlen(name);
- size_t suffixLen = strlen(suffix);
+ size_t suffixLen = suffix.length();
// There can be at most one file that matches this suffix (config key).
if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+ strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
O_RDONLY | O_CLOEXEC);
if (fd != -1) {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index f897b13..9cba926 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1158,6 +1158,7 @@
Landroid/os/Binder;->mObject:J
Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
Landroid/os/Build;->IS_DEBUGGABLE:Z
+Landroid/os/Build;->IS_EMULATOR:Z
Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5d21be5..d3c1e99 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3118,7 +3118,6 @@
private int mActionBarColor = COLOR_INVALID;
private int mBackgroundColor = COLOR_INVALID;
private int mForegroundColor = COLOR_INVALID;
- private int mBackgroundColorHint = COLOR_INVALID;
/**
* A temporary location where actions are stored. If != null the view originally has action
* but doesn't have any for this inflation.
@@ -4387,8 +4386,7 @@
backgroundColor);
mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
backgroundColor);
- if (backgroundColor != COLOR_DEFAULT
- && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
+ if (backgroundColor != COLOR_DEFAULT && isColorized()) {
mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
mPrimaryTextColor, backgroundColor, 4.5);
mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
@@ -4595,21 +4593,13 @@
}
private void bindExpandButton(RemoteViews contentView) {
- int color = getPrimaryHighlightColor();
+ int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
contentView.setDrawableTint(R.id.expand_button, false, color,
PorterDuff.Mode.SRC_ATOP);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
- /**
- * @return the color that is used as the first primary highlight color. This is applied
- * in several places like the action buttons or the app name in the header.
- */
- private int getPrimaryHighlightColor() {
- return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
- }
-
private void bindHeaderChronometerAndTime(RemoteViews contentView) {
if (showsTimeOrChronometer()) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
@@ -4706,7 +4696,7 @@
setTextViewColorPrimary(contentView, R.id.app_name_text);
} else {
contentView.setTextColor(R.id.app_name_text,
- ambient ? resolveAmbientColor() : resolveContrastColor());
+ ambient ? resolveAmbientColor() : getSecondaryTextColor());
}
}
@@ -5234,7 +5224,14 @@
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
boolean ambient) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
- int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
+ int color;
+ if (ambient) {
+ color = resolveAmbientColor();
+ } else if (isColorized()) {
+ color = getPrimaryTextColor();
+ } else {
+ color = resolveContrastColor();
+ }
if (colorable) {
contentView.setDrawableTint(R.id.icon, false, color,
PorterDuff.Mode.SRC_ATOP);
@@ -5270,14 +5267,11 @@
}
int color;
- int background = mBackgroundColorHint;
- if (mBackgroundColorHint == COLOR_INVALID) {
- background = mContext.getColor(
- com.android.internal.R.color.notification_material_background_color);
- }
+ int background = mContext.getColor(
+ com.android.internal.R.color.notification_material_background_color);
if (mN.color == COLOR_DEFAULT) {
ensureColors();
- color = mSecondaryTextColor;
+ color = NotificationColorUtil.resolveDefaultColor(mContext, background);
} else {
color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
background, mInNightMode);
@@ -5517,8 +5511,7 @@
if (isColorized()) {
return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
} else {
- return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
- : COLOR_DEFAULT;
+ return COLOR_DEFAULT;
}
}
@@ -5555,18 +5548,6 @@
}
/**
- * Sets the background color for this notification to be a different one then the default.
- * This is mainly used to calculate contrast and won't necessarily be applied to the
- * background.
- *
- * @hide
- */
- public void setBackgroundColorHint(int backgroundColor) {
- mBackgroundColorHint = backgroundColor;
- }
-
-
- /**
* Forces all styled remoteViews to be built from scratch and not use any cached
* RemoteViews.
* This is needed for legacy apps that are baking in their remoteviews into the
@@ -5972,7 +5953,7 @@
* @hide
*/
public abstract boolean areNotificationsVisiblyDifferent(Style other);
-
+
/**
* @return the the text that should be displayed in the statusBar when heads-upped.
* If {@code null} is returned, the default implementation will be used.
@@ -7498,8 +7479,7 @@
}
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- final RemoteViews button = generateMediaActionButton(action,
- getPrimaryHighlightColor());
+ final RemoteViews button = generateMediaActionButton(action, getActionColor());
view.addView(com.android.internal.R.id.media_actions, button);
}
}
@@ -7513,8 +7493,9 @@
return view;
}
- private int getPrimaryHighlightColor() {
- return mBuilder.getPrimaryHighlightColor();
+ private int getActionColor() {
+ return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
+ : mBuilder.resolveContrastColor();
}
private RemoteViews makeMediaBigContentView() {
@@ -7534,7 +7515,7 @@
big.removeAllViews(com.android.internal.R.id.media_actions);
for (int i = 0; i < actionCount; i++) {
final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
- getPrimaryHighlightColor());
+ getActionColor());
big.addView(com.android.internal.R.id.media_actions, button);
}
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 72db33f..f47d464 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -819,7 +819,8 @@
* @param config A session configuration (see {@link SessionConfiguration}).
*
* @throws IllegalArgumentException In case the session configuration is invalid; or the output
- * configurations are empty.
+ * configurations are empty; or the session configuration
+ * executor is invalid.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a2bc91e..4d64295 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -25,6 +25,7 @@
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
+import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -41,6 +42,11 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
/**
* <p>A system service manager for detecting, characterizing, and connecting to
@@ -123,16 +129,8 @@
*/
public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
@Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
-
- CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
+ CameraManagerGlobal.get().registerAvailabilityCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
}
/**
@@ -170,15 +168,8 @@
* no looper.
*/
public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
- CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+ CameraManagerGlobal.get().registerTorchCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
}
/**
@@ -728,12 +719,13 @@
*/
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
- // Registered availablility callbacks and their handlers
- private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
- new ArrayMap<AvailabilityCallback, Handler>();
+ // Registered availablility callbacks and their executors
+ private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
+ new ArrayMap<AvailabilityCallback, Executor>();
// torch client binder to set the torch mode with.
private Binder mTorchClientBinder = new Binder();
@@ -741,9 +733,9 @@
// Camera ID -> Torch status map
private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
- // Registered torch callbacks and their handlers
- private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
- new ArrayMap<TorchCallback, Handler>();
+ // Registered torch callbacks and their executors
+ private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
+ new ArrayMap<TorchCallback, Executor>();
private final Object mLock = new Object();
@@ -925,49 +917,63 @@
}
}
- private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
+ private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
final String id, final int status) {
if (isAvailable(status)) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraAvailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraAvailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraUnavailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraUnavailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
- private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+ private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
final String id, final int status) {
switch(status) {
case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
- case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeChanged(id, status ==
- ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
- }
+ case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeChanged(id, status ==
+ ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
- default:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeUnavailable(id);
- }
+ default: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeUnavailable(id);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
}
}
@@ -976,11 +982,11 @@
* Send the state of all known cameras to the provided listener, to initialize
* the listener's knowledge of camera state.
*/
- private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+ private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
}
@@ -1039,18 +1045,18 @@
final int callbackCount = mCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- Handler handler = mCallbackMap.valueAt(i);
+ Executor executor = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
} // onStatusChangedLocked
- private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+ private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
for (int i = 0; i < mTorchStatus.size(); i++) {
String id = mTorchStatus.keyAt(i);
Integer status = mTorchStatus.valueAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
}
@@ -1078,9 +1084,9 @@
final int callbackCount = mTorchCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- final Handler handler = mTorchCallbackMap.valueAt(i);
+ final Executor executor = mTorchCallbackMap.valueAt(i);
final TorchCallback callback = mTorchCallbackMap.keyAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
} // onTorchStatusChangedLocked
@@ -1089,16 +1095,16 @@
* global listener singleton.
*
* @param callback the new callback to send camera availability notices to
- * @param handler The handler on which the callback should be invoked. May not be null.
+ * @param executor The executor which should invoke the callback. May not be null.
*/
- public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
synchronized (mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mCallbackMap.put(callback, handler);
+ Executor oldExecutor = mCallbackMap.put(callback, executor);
// For new callbacks, provide initial availability information
- if (oldHandler == null) {
- updateCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1120,14 +1126,14 @@
}
}
- public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ public void registerTorchCallback(TorchCallback callback, Executor executor) {
synchronized(mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+ Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
// For new callbacks, provide initial torch information
- if (oldHandler == null) {
- updateTorchCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateTorchCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1165,13 +1171,7 @@
* availability callback or torch status callback.
*/
private void scheduleCameraServiceReconnectionLocked() {
- final Handler handler;
-
- if (mCallbackMap.size() > 0) {
- handler = mCallbackMap.valueAt(0);
- } else if (mTorchCallbackMap.size() > 0) {
- handler = mTorchCallbackMap.valueAt(0);
- } else {
+ if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
// Not necessary to reconnect camera service if no client registers a callback.
return;
}
@@ -1181,22 +1181,21 @@
" ms");
}
- handler.postDelayed(
- new Runnable() {
- @Override
- public void run() {
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Reconnecting Camera Service failed.");
- }
- scheduleCameraServiceReconnectionLocked();
- }
+ try {
+ mScheduler.schedule(() -> {
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.v(TAG, "Reconnecting Camera Service failed.");
}
+ scheduleCameraServiceReconnectionLocked();
}
- },
- CAMERA_SERVICE_RECONNECT_DELAY_MS);
+ }
+ }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
deleted file mode 100644
index 866f370..0000000
--- a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * A dispatcher that replaces one argument with another; replaces any argument at an index
- * with another argument.
- *
- * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
- * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
- * be something
- * that's not an {@code int}.</p>
- *
- * @param <T>
- * source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <TArg>
- * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
- * will be overriden to objects of this type
- */
-public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
-
- private final Dispatchable<T> mTarget;
- private final int mArgumentIndex;
- private final TArg mReplaceWith;
-
- /**
- * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
- * after the argument is replaced.
- *
- * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
- * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
- * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
- *
- * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
- * passed through with the arguments unchanged.</p>
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param argumentIndex the numeric index of the argument {@code >= 0}
- * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
- */
- public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
- TArg replaceWith) {
- mTarget = checkNotNull(target, "target must not be null");
- mArgumentIndex = checkArgumentNonnegative(argumentIndex,
- "argumentIndex must not be negative");
- mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
-
- if (args.length > mArgumentIndex) {
- args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
- args[mArgumentIndex] = mReplaceWith;
- }
-
- return mTarget.dispatch(method, args);
- }
-
- private static Object[] arrayCopy(Object[] array) {
- int length = array.length;
- Object[] newArray = new Object[length];
- for (int i = 0; i < length; ++i) {
- newArray[i] = array[i];
- }
- return newArray;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
deleted file mode 100644
index fe575b2..0000000
--- a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Broadcast a single dispatch into multiple other dispatchables.
- *
- * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
- * see the same dispatch as well. The first target's return value is returned.</p>
- *
- * <p>This enables a single listener to be converted into a multi-listener.</p>
- */
-public class BroadcastDispatcher<T> implements Dispatchable<T> {
-
- private final List<Dispatchable<T>> mDispatchTargets;
-
- /**
- * Create a broadcast dispatcher from the supplied dispatch targets.
- *
- * @param dispatchTargets one or more targets to dispatch to
- */
- @SafeVarargs
- public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
- mDispatchTargets = Arrays.asList(
- checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
- Object result = null;
- boolean gotResult = false;
-
- for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
- Object localResult = dispatchTarget.dispatch(method, args);
-
- if (!gotResult) {
- gotResult = true;
- result = localResult;
- }
- }
-
- return result;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
deleted file mode 100644
index 753103f..0000000
--- a/core/java/android/hardware/camera2/dispatch/Dispatchable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-/**
- * Dynamically dispatch a method and its argument to some object.
- *
- * <p>This can be used to intercept method calls and do work around them, redirect work,
- * or block calls entirely.</p>
- */
-public interface Dispatchable<T> {
- /**
- * Dispatch the method and arguments to this object.
- * @param method a method defined in class {@code T}
- * @param args arguments corresponding to said {@code method}
- * @return the object returned when invoking {@code method}
- * @throws Throwable any exception that might have been raised while invoking the method
- */
- public Object dispatch(Method method, Object[] args) throws Throwable;
-}
diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
deleted file mode 100644
index 75f97e4..0000000
--- a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Duck typing dispatcher; converts dispatch methods calls from one class to another by
- * looking up equivalently methods at runtime by name.
- *
- * <p>For example, if two types have identical method names and arguments, but
- * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
- * made from one type to the other.</p>
- *
- * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
- */
-public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
-
- private final MethodNameInvoker<T> mDuck;
-
- /**
- * Create a new duck typing dispatcher.
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param targetClass destination dispatch class, methods will be converted to this class's
- */
- public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
- checkNotNull(targetClass, "targetClass must not be null");
- checkNotNull(target, "target must not be null");
-
- mDuck = new MethodNameInvoker<T>(target, targetClass);
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- return mDuck.invoke(method.getName(), args);
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
deleted file mode 100644
index f8e9d49..0000000
--- a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.os.Handler;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Forward all interface calls into a handler by posting it as a {@code Runnable}.
- *
- * <p>All calls will return immediately; functions with return values will return a default
- * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
- *
- * <p>Any exceptions thrown on the handler while trying to invoke a method
- * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
- * checked exceptions to be thrown will result in "undefined" behavior
- * (although in practice it is usually thrown as normal).</p>
- */
-public class HandlerDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "HandlerDispatcher";
-
- private final Dispatchable<T> mDispatchTarget;
- private final Handler mHandler;
-
- /**
- * Create a dispatcher that forwards it's dispatch calls by posting
- * them onto the {@code handler} as a {@code Runnable}.
- *
- * @param dispatchTarget the destination whose method calls will be redirected into the handler
- * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
- * @param <T> the type of the element you want to wrap.
- * @return a dispatcher that will forward it's dispatch calls to a handler
- */
- public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
- mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mHandler = checkNotNull(handler, "handler must not be null");
- }
-
- @Override
- public Object dispatch(final Method method, final Object[] args) throws Throwable {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- mDispatchTarget.dispatch(method, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- }
- }
- });
-
- // TODO handle primitive return values that would avoid NPE if unboxed
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
deleted file mode 100644
index ac5f526..0000000
--- a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-
-public class InvokeDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "InvocationSink";
- private final T mTarget;
-
- public InvokeDispatcher(T target) {
- mTarget = checkNotNull(target, "target must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- try {
- return method.invoke(mTarget, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- }
-
- // unreachable
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
deleted file mode 100644
index 8296b7a..0000000
--- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
- *
- * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
- */
-public class MethodNameInvoker<T> {
-
- private final Dispatchable<T> mTarget;
- private final Class<T> mTargetClass;
- private final Method[] mTargetClassMethods;
- private final ConcurrentHashMap<String, Method> mMethods =
- new ConcurrentHashMap<>();
-
- /**
- * Create a new method name invoker.
- *
- * @param target destination dispatch type, invokes will be redirected to this dispatcher
- * @param targetClass destination dispatch class, the invoked methods will be from this class
- */
- public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
- mTargetClass = targetClass;
- mTargetClassMethods = targetClass.getMethods();
- mTarget = target;
- }
-
- /**
- * Invoke a method by its name.
- *
- * <p>If more than one method exists in {@code targetClass}, the first method with the right
- * number of arguments will be used, and later calls will all use that method.</p>
- *
- * @param methodName
- * The name of the method, which will be matched 1:1 to the destination method
- * @param params
- * Variadic parameter list.
- * @return
- * The same kind of value that would normally be returned by calling {@code methodName}
- * statically.
- *
- * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
- * @throws Throwable will rethrow anything that the target method would normally throw
- */
- @SuppressWarnings("unchecked")
- public <K> K invoke(String methodName, Object... params) {
- checkNotNull(methodName, "methodName must not be null");
-
- Method targetMethod = mMethods.get(methodName);
- if (targetMethod == null) {
- for (Method method : mTargetClassMethods) {
- // TODO future: match types of params if possible
- if (method.getName().equals(methodName) &&
- (params.length == method.getParameterTypes().length) ) {
- targetMethod = method;
- mMethods.put(methodName, targetMethod);
- break;
- }
- }
-
- if (targetMethod == null) {
- throw new IllegalArgumentException(
- "Method " + methodName + " does not exist on class " + mTargetClass);
- }
- }
-
- try {
- return (K) mTarget.dispatch(targetMethod, params);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- // unreachable
- return null;
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
deleted file mode 100644
index fada075..0000000
--- a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-/**
- * Do nothing when dispatching; follows the null object pattern.
- */
-public class NullDispatcher<T> implements Dispatchable<T> {
- /**
- * Create a dispatcher that does nothing when dispatched to.
- */
- public NullDispatcher() {
- }
-
- /**
- * Do nothing; all parameters are ignored.
- */
- @Override
- public Object dispatch(Method method, Object[] args) {
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html
deleted file mode 100644
index 783d0a1..0000000
--- a/core/java/android/hardware/camera2/dispatch/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-{@hide}
-</body>
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index c9eecf1..9e4cb80 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,16 +15,17 @@
*/
package android.hardware.camera2.impl;
+import android.os.Binder;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.dispatch.Dispatchable;
-import android.hardware.camera2.dispatch.MethodNameInvoker;
import android.view.Surface;
+import java.util.concurrent.Executor;
+
import static com.android.internal.util.Preconditions.*;
/**
@@ -34,164 +35,86 @@
* to use our own proxy mechanism.</p>
*/
public class CallbackProxies {
-
- // TODO: replace with codegen
-
- public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
- private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
-
- public DeviceStateCallbackProxy(
- Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
- }
-
- @Override
- public void onOpened(CameraDevice camera) {
- mProxy.invoke("onOpened", camera);
- }
-
- @Override
- public void onDisconnected(CameraDevice camera) {
- mProxy.invoke("onDisconnected", camera);
- }
-
- @Override
- public void onError(CameraDevice camera, int error) {
- mProxy.invoke("onError", camera, error);
- }
-
- @Override
- public void onUnconfigured(CameraDevice camera) {
- mProxy.invoke("onUnconfigured", camera);
- }
-
- @Override
- public void onActive(CameraDevice camera) {
- mProxy.invoke("onActive", camera);
- }
-
- @Override
- public void onBusy(CameraDevice camera) {
- mProxy.invoke("onBusy", camera);
- }
-
- @Override
- public void onClosed(CameraDevice camera) {
- mProxy.invoke("onClosed", camera);
- }
-
- @Override
- public void onIdle(CameraDevice camera) {
- mProxy.invoke("onIdle", camera);
- }
- }
-
- @SuppressWarnings("deprecation")
- public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
- private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
-
- public DeviceCaptureCallbackProxy(
- Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
- }
-
- @Override
- public void onCaptureStarted(CameraDevice camera,
- CaptureRequest request, long timestamp, long frameNumber) {
- mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
- }
-
- @Override
- public void onCapturePartial(CameraDevice camera,
- CaptureRequest request, CaptureResult result) {
- mProxy.invoke("onCapturePartial", camera, request, result);
- }
-
- @Override
- public void onCaptureProgressed(CameraDevice camera,
- CaptureRequest request, CaptureResult partialResult) {
- mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraDevice camera,
- CaptureRequest request, TotalCaptureResult result) {
- mProxy.invoke("onCaptureCompleted", camera, request, result);
- }
-
- @Override
- public void onCaptureFailed(CameraDevice camera,
- CaptureRequest request, CaptureFailure failure) {
- mProxy.invoke("onCaptureFailed", camera, request, failure);
- }
-
- @Override
- public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, long frameNumber) {
- mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
- }
-
- @Override
- public void onCaptureSequenceAborted(CameraDevice camera,
- int sequenceId) {
- mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
- }
-
- @Override
- public void onCaptureBufferLost(CameraDevice camera,
- CaptureRequest request, Surface target, long frameNumber) {
- mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
- }
-
- }
-
public static class SessionStateCallbackProxy
extends CameraCaptureSession.StateCallback {
- private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
+ private final Executor mExecutor;
+ private final CameraCaptureSession.StateCallback mCallback;
- public SessionStateCallbackProxy(
- Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget,
- CameraCaptureSession.StateCallback.class);
+ public SessionStateCallbackProxy(Executor executor,
+ CameraCaptureSession.StateCallback callback) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
+ mCallback = checkNotNull(callback, "callback must not be null");
}
@Override
public void onConfigured(CameraCaptureSession session) {
- mProxy.invoke("onConfigured", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigured(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
- mProxy.invoke("onConfigureFailed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigureFailed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onReady(CameraCaptureSession session) {
- mProxy.invoke("onReady", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onReady(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onActive(CameraCaptureSession session) {
- mProxy.invoke("onActive", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onActive(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onCaptureQueueEmpty(CameraCaptureSession session) {
- mProxy.invoke("onCaptureQueueEmpty", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onClosed(CameraCaptureSession session) {
- mProxy.invoke("onClosed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onClosed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
- mProxy.invoke("onSurfacePrepared", session, surface);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 8b8bbc3..9cac71c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,20 +20,17 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
-import android.hardware.camera2.dispatch.BroadcastDispatcher;
-import android.hardware.camera2.dispatch.DuckTypingDispatcher;
-import android.hardware.camera2.dispatch.HandlerDispatcher;
-import android.hardware.camera2.dispatch.InvokeDispatcher;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.TaskDrainer;
import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Binder;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
import static com.android.internal.util.Preconditions.*;
@@ -51,16 +48,16 @@
private final Surface mInput;
/**
* User-specified state callback, used for outgoing events; calls to this object will be
- * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+ * automatically invoked via {@code mStateExecutor}.
*/
private final CameraCaptureSession.StateCallback mStateCallback;
- /** User-specified state handler used for outgoing state callback events */
- private final Handler mStateHandler;
+ /** User-specified state executor used for outgoing state callback events */
+ private final Executor mStateExecutor;
/** Internal camera device; used to translate calls into existing deprecated API */
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
- /** Internal handler; used for all incoming events to preserve total order */
- private final Handler mDeviceHandler;
+ /** Internal executor; used for all incoming events to preserve total order */
+ private final Executor mDeviceExecutor;
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
private final TaskDrainer<Integer> mSequenceDrainer;
@@ -87,9 +84,9 @@
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraCaptureSessionImpl(int id, Surface input,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess) {
+ Executor deviceStateExecutor, boolean configureSuccess) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
@@ -98,10 +95,11 @@
mIdString = String.format("Session %d: ", mId);
mInput = input;
- mStateHandler = checkHandler(stateHandler);
- mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
+ mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
+ mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
- mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+ mDeviceExecutor = checkNotNull(deviceStateExecutor,
+ "deviceStateExecutor must not be null");
mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
/*
@@ -110,11 +108,11 @@
* This ensures total ordering between CameraDevice.StateCallback and
* CameraDeviceImpl.CaptureCallback events.
*/
- mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+ mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
/*name*/"seq");
- mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+ mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
/*name*/"idle");
- mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+ mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
/*name*/"abort");
// CameraDevice should call configureOutputs and have it finish before constructing us
@@ -180,7 +178,7 @@
}
return addPendingSequence(mDeviceImpl.capture(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -217,7 +215,7 @@
}
return addPendingSequence(mDeviceImpl.captureBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -241,7 +239,7 @@
}
return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -274,7 +272,7 @@
}
return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -446,119 +444,145 @@
}
/**
- * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
+ * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
*/
- private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
- InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
- HandlerDispatcher<StateCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
-
- return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
+ private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
+ return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
}
/**
* Forward callbacks from
* CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
*
- * <p>In particular, all calls are automatically split to go both to our own
- * internal callback, and to the user-specified callback (by transparently posting
- * to the user-specified handler).</p>
- *
* <p>When a capture sequence finishes, update the pending checked sequences set.</p>
*/
@SuppressWarnings("deprecation")
private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
Handler handler, CaptureCallback callback) {
- CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+ final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
+ handler) : null;
+ return new CameraDeviceImpl.CaptureCallback() {
@Override
public void onCaptureStarted(CameraDevice camera,
CaptureRequest request, long timestamp, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureStarted(
+ CameraCaptureSessionImpl.this, request, timestamp,
+ frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCapturePartial(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCapturePartial(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureProgressed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureProgressed(
+ CameraCaptureSessionImpl.this, request, partialResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureCompleted(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureCompleted(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureFailed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureFailed(
+ CameraCaptureSessionImpl.this, request, failure));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceCompleted(
+ CameraCaptureSessionImpl.this, sequenceId, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureSequenceAborted(CameraDevice camera,
int sequenceId) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceAborted(
+ CameraCaptureSessionImpl.this, sequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureBufferLost(CameraDevice camera,
CaptureRequest request, Surface target, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureBufferLost(
+ CameraCaptureSessionImpl.this, request, target, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
-
};
-
- /*
- * Split the calls from the device callback into local callback and the following chain:
- * - replace the first CameraDevice arg with a CameraCaptureSession
- * - duck type from device callback to session callback
- * - then forward the call to a handler
- * - then finally invoke the destination method on the session callback object
- */
- if (callback == null) {
- // OK: API allows the user to not specify a callback, and the handler may
- // also be null in that case. Collapse whole dispatch chain to only call the local
- // callback
- return localCallback;
- }
-
- InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
- new InvokeDispatcher<>(localCallback);
-
- InvokeDispatcher<CaptureCallback> userCallbackSink =
- new InvokeDispatcher<>(callback);
- HandlerDispatcher<CaptureCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
- DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
- = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
- ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
- replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
- /*argumentIndex*/0, this);
-
- BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
- new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
- replaceDeviceWithSession,
- localSink);
-
- return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
}
/**
*
- * Create an internal state callback, to be invoked on the mDeviceHandler
+ * Create an internal state callback, to be invoked on the mDeviceExecutor
*
* <p>It has a few behaviors:
* <ul>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 06c2c25..89f6172 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,6 +33,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -59,14 +60,14 @@
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraConstrainedHighSpeedCaptureSessionImpl(int id,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess,
+ Executor deviceStateExecutor, boolean configureSuccess,
CameraCharacteristics characteristics) {
mCharacteristics = characteristics;
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
- stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+ stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
}
@Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 511fa43..1f35f31 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import android.annotation.NonNull;
import android.hardware.ICameraService;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -35,6 +36,7 @@
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -47,6 +49,8 @@
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.util.Preconditions;
+
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,6 +62,8 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executor;
+
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,7 +84,7 @@
private final StateCallback mDeviceCallback;
private volatile StateCallbackKK mSessionStateCallback;
- private final Handler mDeviceHandler;
+ private final Executor mDeviceExecutor;
private final AtomicBoolean mClosing = new AtomicBoolean();
private boolean mInError = false;
@@ -241,7 +247,7 @@
}
mCameraId = cameraId;
mDeviceCallback = callback;
- mDeviceHandler = handler;
+ mDeviceExecutor = checkAndWrapHandler(handler);
mCharacteristics = characteristics;
mAppTargetSdkVersion = appTargetSdkVersion;
@@ -288,15 +294,15 @@
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
- mDeviceHandler.post(mCallOnOpened);
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnOpened);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
@@ -335,7 +341,7 @@
final boolean isError = failureIsError;
synchronized(mInterfaceLock) {
mInError = true;
- mDeviceHandler.post(new Runnable() {
+ mDeviceExecutor.execute(new Runnable() {
@Override
public void run() {
if (isError) {
@@ -423,7 +429,7 @@
}
}
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
stopRepeating();
try {
@@ -482,10 +488,10 @@
throw e;
} finally {
if (success && outputs.size() > 0) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
} else {
// Always return to the 'unconfigured' state if we didn't hit a fatal error
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
}
@@ -501,8 +507,9 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -517,7 +524,7 @@
// OutputConfiguration objects are immutable, but need to have our own array
List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
- createCaptureSessionInternal(null, currentOutputs, callback, handler,
+ createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
}
@@ -537,8 +544,9 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -566,8 +574,8 @@
currentOutputs.add(new OutputConfiguration(output));
}
createCaptureSessionInternal(inputConfig, currentOutputs,
- callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
- /*sessionParams*/ null);
+ callback, checkAndWrapHandler(handler),
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
}
@Override
@@ -582,7 +590,8 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
/*sessionParams*/ null);
}
@@ -597,8 +606,8 @@
for (OutputConfiguration output : outputs) {
currentOutputs.add(new OutputConfiguration(output));
}
- createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
- /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, currentOutputs, callback,
+ checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
}
@Override
@@ -612,14 +621,17 @@
if (outputConfigs == null) {
throw new IllegalArgumentException("Invalid output configurations");
}
+ if (config.getExecutor() == null) {
+ throw new IllegalArgumentException("Invalid executor");
+ }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
- config.getStateCallback(), config.getHandler(), config.getSessionType(),
+ config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
}
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
- CameraCaptureSession.StateCallback callback, Handler handler,
+ CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
@@ -673,12 +685,11 @@
SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
- callback, handler, this, mDeviceHandler, configureSuccess,
+ callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
- callback, handler, this, mDeviceHandler,
- configureSuccess);
+ callback, executor, this, mDeviceExecutor, configureSuccess);
}
// TODO: wait until current session closes, then create the new session
@@ -893,22 +904,22 @@
}
}
- public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
+ public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
throws CameraAccessException {
if (DEBUG) {
Log.d(TAG, "calling capture");
}
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
}
public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
}
/**
@@ -963,7 +974,12 @@
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
Log.w(TAG, String.format(
"did not register callback to request %d",
@@ -982,11 +998,11 @@
}
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
- Handler handler, boolean repeating) throws CameraAccessException {
+ Executor executor, boolean repeating) throws CameraAccessException {
- // Need a valid handler, or current thread needs to have a looper, if
+ // Need a valid executor, or current thread needs to have a looper, if
// callback is valid
- handler = checkHandler(handler, callback);
+ executor = checkExecutor(executor, callback);
// Make sure that there all requests have at least 1 surface; all surfaces are non-null;
// the surface isn't a physical stream surface for reprocessing request
@@ -1040,7 +1056,7 @@
if (callback != null) {
mCaptureCallbackMap.put(requestInfo.getRequestId(),
new CaptureCallbackHolder(
- callback, requestList, handler, repeating, mNextSessionId - 1));
+ callback, requestList, executor, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1059,7 +1075,7 @@
}
if (mIdle) {
- mDeviceHandler.post(mCallOnActive);
+ mDeviceExecutor.execute(mCallOnActive);
}
mIdle = false;
@@ -1068,18 +1084,18 @@
}
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
}
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
}
public void stopRepeating() throws CameraAccessException {
@@ -1124,12 +1140,12 @@
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
// If already idle, just do a busy->idle transition immediately, don't actually
// flush.
if (mIdle) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
return;
}
@@ -1157,7 +1173,7 @@
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
if (mRemoteDevice != null || mInError) {
- mDeviceHandler.post(mCallOnClosed);
+ mDeviceExecutor.execute(mCallOnClosed);
}
mRemoteDevice = null;
@@ -1354,7 +1370,7 @@
private final boolean mRepeating;
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final int mSessionId;
/**
* <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1366,13 +1382,13 @@
private final boolean mHasBatchedOutputs;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating, int sessionId) {
- if (callback == null || handler == null) {
+ Executor executor, boolean repeating, int sessionId) {
+ if (callback == null || executor == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
}
mRepeating = repeating;
- mHandler = handler;
+ mExecutor = executor;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
mSessionId = sessionId;
@@ -1425,8 +1441,8 @@
return getRequest(0);
}
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
public int getSessionId() {
@@ -1810,7 +1826,12 @@
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -1838,7 +1859,12 @@
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
break;
case ERROR_CAMERA_REQUEST:
case ERROR_CAMERA_RESULT:
@@ -1860,8 +1886,13 @@
private void scheduleNotifyError(int code) {
mInError = true;
- CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
- CameraDeviceCallbacks::notifyError, this, code));
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
+ CameraDeviceCallbacks::notifyError, this, code));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void notifyError(int code) {
@@ -1900,7 +1931,12 @@
if (mRemoteDevice == null) return; // Camera already closed
if (!CameraDeviceImpl.this.mIdle) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
CameraDeviceImpl.this.mIdle = true;
}
@@ -1929,36 +1965,41 @@
if (isClosed()) return;
// Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- final int subsequenceId = resultExtras.getSubsequenceId();
- final CaptureRequest request = holder.getRequest(subsequenceId);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ final int subsequenceId = resultExtras.getSubsequenceId();
+ final CaptureRequest request = holder.getRequest(subsequenceId);
- if (holder.hasBatchedOutputs()) {
- // Send derived onCaptureStarted for requests within the batch
- final Range<Integer> fpsRange =
- request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
- for (int i = 0; i < holder.getRequestCount(); i++) {
+ if (holder.hasBatchedOutputs()) {
+ // Send derived onCaptureStarted for requests within the
+ // batch
+ final Range<Integer> fpsRange =
+ request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ for (int i = 0; i < holder.getRequestCount(); i++) {
+ holder.getCallback().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(i),
+ timestamp - (subsequenceId - i) *
+ NANO_PER_SECOND/fpsRange.getUpper(),
+ frameNumber - (subsequenceId - i));
+ }
+ } else {
holder.getCallback().onCaptureStarted(
CameraDeviceImpl.this,
- holder.getRequest(i),
- timestamp - (subsequenceId - i) *
- NANO_PER_SECOND/fpsRange.getUpper(),
- frameNumber - (subsequenceId - i));
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp, frameNumber);
}
- } else {
- holder.getCallback().onCaptureStarted(
- CameraDeviceImpl.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp, frameNumber);
}
}
- }
- });
-
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2111,7 +2152,12 @@
finalResult = resultAsCapture;
}
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
// Collect the partials for a total result; or mark the frame as totally completed
mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2207,7 +2253,12 @@
}
};
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
} else {
boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2247,7 +2298,12 @@
checkAndFireSequenceComplete();
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2255,6 +2311,62 @@
} // public class CameraDeviceCallbacks
/**
+ * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+ private static class CameraHandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public CameraHandlerExecutor(@NonNull Handler handler) {
+ mHandler = Preconditions.checkNotNull(handler);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ // Return value of 'post()' will be ignored in order to keep the
+ // same camera behavior. For further details see b/74605221 .
+ mHandler.post(command);
+ }
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>
+ * If executor is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ static Executor checkExecutor(Executor executor) {
+ return (executor == null) ? checkAndWrapHandler(null) : executor;
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
+ */
+ static <T> Executor checkExecutor(Executor executor, T callback) {
+ return (callback != null) ? checkExecutor(executor) : executor;
+ }
+
+ /**
+ * Wrap Handler in Executor.
+ *
+ * <p>
+ * If handler is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ public static Executor checkAndWrapHandler(Handler handler) {
+ return new CameraHandlerExecutor(checkHandler(handler));
+ }
+
+ /**
* Default handler management.
*
* <p>
@@ -2328,6 +2440,11 @@
}
}
};
- CameraDeviceImpl.this.mDeviceHandler.post(r);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(r);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index a79a6c1..7bdb4a2 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
package android.hardware.camera2.params;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
-import android.os.Handler;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -78,7 +79,7 @@
private List<OutputConfiguration> mOutputConfigurations;
private CameraCaptureSession.StateCallback mStateCallback;
private int mSessionType;
- private Handler mHandler = null;
+ private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
private CaptureRequest mSessionParameters = null;
@@ -87,10 +88,9 @@
*
* @param sessionType The session type.
* @param outputs A list of output configurations for the capture session.
+ * @param executor The executor which should be used to invoke the callback. In general it is
+ * recommended that camera operations are not done on the main (UI) thread.
* @param cb A state callback interface implementation.
- * @param handler The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
*
* @see #SESSION_REGULAR
* @see #SESSION_HIGH_SPEED
@@ -101,11 +101,12 @@
*/
public SessionConfiguration(@SessionMode int sessionType,
@NonNull List<OutputConfiguration> outputs,
- @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
mSessionType = sessionType;
mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
mStateCallback = cb;
- mHandler = handler;
+ mExecutor = executor;
}
/**
@@ -136,14 +137,12 @@
}
/**
- * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+ * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
*
- * @return The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
+ * @return The Executor on which the callback will be invoked.
*/
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
/**
diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java
index ed30ff3..e71f26a 100644
--- a/core/java/android/hardware/camera2/utils/TaskDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
*/
package android.hardware.camera2.utils;
-import android.os.Handler;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -55,7 +55,7 @@
private static final String TAG = "TaskDrainer";
private final boolean DEBUG = false;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final DrainListener mListener;
private final String mName;
@@ -73,28 +73,27 @@
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskDrainer(Handler handler, DrainListener listener) {
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = null;
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskDrainer(Handler handler, DrainListener listener, String name) {
- // XX: Probably don't need a handler at all here
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener, String name) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = name;
}
@@ -200,15 +199,12 @@
}
private void postDrained() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ mExecutor.execute(() -> {
if (DEBUG) {
Log.v(TAG + "[" + mName + "]", "onDrained");
}
mListener.onDrained();
- }
});
}
}
diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
index f6272c9..9615450 100644
--- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,7 +16,8 @@
package android.hardware.camera2.utils;
import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-import android.os.Handler;
+
+import java.util.concurrent.Executor;
/**
* Keep track of a single concurrent task starting and finishing;
@@ -38,25 +39,25 @@
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener) {
- mTaskDrainer = new TaskDrainer<>(handler, listener);
+ public TaskSingleDrainer(Executor executor, DrainListener listener) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener);
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
- mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+ public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener, name);
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 36f359b..93b1b22 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1977,13 +1977,6 @@
* services.jar, possibly in com.android.server.net. */
/** {@hide} */
- public static final boolean checkChangePermission(Context context) {
- int uid = Binder.getCallingUid();
- return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
- .getPackageNameForUid(context, uid), false /* throwException */);
- }
-
- /** {@hide} */
public static final void enforceChangePermission(Context context) {
int uid = Binder.getCallingUid();
Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index fdcc304..4f92fa6 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -24,6 +24,7 @@
import android.util.proto.ProtoOutputStream;
import java.util.Objects;
+import java.util.Set;
/**
* Defines a request for a network, made through {@link NetworkRequest.Builder} and used
@@ -205,6 +206,19 @@
}
/**
+ * Set the watched UIDs for this request. This will be reset and wiped out unless
+ * the calling app holds the CHANGE_NETWORK_STATE permission.
+ *
+ * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+ * @return The builder to facilitate chaining.
+ * @hide
+ */
+ public Builder setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ return this;
+ }
+
+ /**
* Add a capability that must not exist in the requested network.
* <p>
* If the capability was previously added to the list of required capabilities (for
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 9ac2b3e..3bf09ec 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -20,6 +20,10 @@
import com.google.android.rappor.Encoder;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
@@ -66,7 +70,7 @@
random = sSecureRandom;
} else {
// To have deterministic result by hard coding encoder id as seed.
- random = new Random((long) config.mEncoderId.hashCode());
+ random = new Random(getInsecureSeed(config.mEncoderId));
userSecret = INSECURE_SECRET;
}
mEncoder = new Encoder(random, null, null,
@@ -75,6 +79,17 @@
config.mNumCohorts, config.mNumBloomHashes);
}
+ private long getInsecureSeed(String input) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+ return ByteBuffer.wrap(bytes).getLong();
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen
+ throw new AssertionError("Unable generate insecure seed");
+ }
+ }
+
/**
* Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
*
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index 3d3b6d5..d42424e 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -52,7 +52,7 @@
public final class KeyChainProtectionParams implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+ @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
public @interface UserSecretType {
}
@@ -61,11 +61,6 @@
*/
public static final int TYPE_LOCKSCREEN = 100;
- /**
- * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
- */
- public static final int TYPE_CUSTOM_PASSWORD = 101;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"UI_FORMAT_"}, value = {UI_FORMAT_PIN, UI_FORMAT_PASSWORD, UI_FORMAT_PATTERN})
@@ -120,7 +115,6 @@
/**
* @see TYPE_LOCKSCREEN
- * @see TYPE_CUSTOM_PASSWORD
*/
public @UserSecretType int getUserSecretType() {
return mUserSecretType;
@@ -166,7 +160,6 @@
* Sets user secret type.
*
* @see TYPE_LOCKSCREEN
- * @see TYPE_CUSTOM_PASSWORD
* @param userSecretType The secret type
* @return This builder.
*/
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 00f54e1..69b9123 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -127,18 +127,13 @@
/**
* CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
*/
- // TODO: Change to @NonNull
- public CertPath getTrustedHardwareCertPath() {
- if (mCertPath == null) {
- return null;
- } else {
- try {
- return mCertPath.getCertPath();
- } catch (CertificateException e) {
- // Rethrow an unchecked exception as it should not happen. If such an issue exists,
- // an exception should have been thrown during service initialization.
- throw new BadParcelableException(e);
- }
+ public @NonNull CertPath getTrustedHardwareCertPath() {
+ try {
+ return mCertPath.getCertPath();
+ } catch (CertificateException e) {
+ // Rethrow an unchecked exception as it should not happen. If such an issue exists,
+ // an exception should have been thrown during service initialization.
+ throw new BadParcelableException(e);
}
}
@@ -248,13 +243,9 @@
* @throws CertificateException if the given certificate path cannot be encoded properly
* @return This builder.
*/
- public Builder setTrustedHardwareCertPath(CertPath certPath) throws CertificateException {
- // TODO: Make it NonNull when the caller code is all updated
- if (certPath == null) {
- mInstance.mCertPath = null;
- } else {
- mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
- }
+ public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
+ throws CertificateException {
+ mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
return this;
}
@@ -282,7 +273,7 @@
}
/**
- * Sets recovery key blob
+ * Sets recovery key blob.
*
* @param encryptedRecoveryKeyBlob The recovery key blob.
* @return This builder.
@@ -297,7 +288,7 @@
* Creates a new {@link KeyChainSnapshot} instance.
*
* @return new instance
- * @throws NullPointerException if some required fields were not set.
+ * @throws NullPointerException if some of the required fields were not set.
*/
@NonNull public KeyChainSnapshot build() {
Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
@@ -306,6 +297,7 @@
"entryRecoveryData");
Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
Preconditions.checkNotNull(mInstance.mServerParams);
+ Preconditions.checkNotNull(mInstance.mCertPath);
return mInstance;
}
}
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index 428eaaa..8cb8e51 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -38,7 +38,7 @@
public final class KeyDerivationParams implements Parcelable {
private final int mAlgorithm;
private final byte[] mSalt;
- private final int mDifficulty;
+ private final int mMemoryDifficulty;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -53,25 +53,32 @@
/**
* SCRYPT.
- *
- * @hide
*/
public static final int ALGORITHM_SCRYPT = 2;
/**
- * Creates instance of the class to to derive key using salted SHA256 hash.
+ * Creates instance of the class to to derive keys using salted SHA256 hash.
+ *
+ * <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
+ * salt + key_material_len + key_material, where salt_len and key_material_len are one-byte, and
+ * denote the number of bytes for salt and key_material, respectively.
*/
public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
return new KeyDerivationParams(ALGORITHM_SHA256, salt);
}
/**
- * Creates instance of the class to to derive key using the password hashing algorithm SCRYPT.
+ * Creates instance of the class to to derive keys using the password hashing algorithm SCRYPT.
*
- * @hide
+ * <p>We expose only one tuning parameter of SCRYPT, which is the memory cost parameter (i.e. N
+ * in <a href="https://www.tarsnap.com/scrypt/scrypt.pdf">the SCRYPT paper</a>). Regular/default
+ * values are used for the other parameters, to keep the overall running time low. Specifically,
+ * the parallelization parameter p is 1, the block size parameter r is 8, and the hashing output
+ * length is 32-byte.
*/
- public static KeyDerivationParams createScryptParams(@NonNull byte[] salt, int difficulty) {
- return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, difficulty);
+ public static KeyDerivationParams createScryptParams(
+ @NonNull byte[] salt, int memoryDifficulty) {
+ return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, memoryDifficulty);
}
/**
@@ -79,17 +86,17 @@
*/
// TODO: Make private once legacy API is removed
public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
- this(algorithm, salt, /*difficulty=*/ 0);
+ this(algorithm, salt, /*memoryDifficulty=*/ -1);
}
/**
* @hide
*/
KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
- int difficulty) {
+ int memoryDifficulty) {
mAlgorithm = algorithm;
mSalt = Preconditions.checkNotNull(salt);
- mDifficulty = difficulty;
+ mMemoryDifficulty = memoryDifficulty;
}
/**
@@ -107,12 +114,16 @@
}
/**
- * Gets hashing difficulty.
+ * Gets the memory difficulty parameter for the hashing algorithm.
*
- * @hide
+ * <p>The effect of this parameter depends on the algorithm in use. For example, please see
+ * {@link #createScryptParams(byte[], int)} for choosing the parameter for SCRYPT.
+ *
+ * <p>If the specific algorithm does not support such a memory difficulty parameter, its value
+ * should be -1.
*/
- public int getDifficulty() {
- return mDifficulty;
+ public int getMemoryDifficulty() {
+ return mMemoryDifficulty;
}
public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
@@ -130,7 +141,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mAlgorithm);
out.writeByteArray(mSalt);
- out.writeInt(mDifficulty);
+ out.writeInt(mMemoryDifficulty);
}
/**
@@ -139,7 +150,7 @@
protected KeyDerivationParams(Parcel in) {
mAlgorithm = in.readInt();
mSalt = in.createByteArray();
- mDifficulty = in.readInt();
+ mMemoryDifficulty = in.readInt();
}
@Override
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 503387a..aa9d1c0 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -266,8 +266,8 @@
/**
* Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
- * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
- * most one registered listener at any time.
+ * #getKeyChainSnapshot} can be used to get the snapshot. Note that every recovery agent can
+ * have at most one registered listener at any time.
*
* @param intent triggered when new snapshot is available. Unregisters listener if the value is
* {@code null}.
@@ -292,12 +292,13 @@
* in vaultParams {@link RecoverySession#start(CertPath, byte[], byte[], List)}.
*
* @param serverParams included in recovery key blob.
- * @see #getRecoveryData
+ * @see #getKeyChainSnapshot
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+ public void setServerParams(@NonNull byte[] serverParams)
+ throws InternalRecoveryServiceException {
try {
mBinder.setServerParams(serverParams);
} catch (RemoteException e) {
@@ -321,7 +322,7 @@
* Returns a list of aliases of keys belonging to the application.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public List<String> getAliases() throws InternalRecoveryServiceException {
+ public @NonNull List<String> getAliases() throws InternalRecoveryServiceException {
try {
Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
return new ArrayList<>(allStatuses.keySet());
@@ -355,7 +356,7 @@
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void setRecoveryStatus(String alias, int status)
+ public void setRecoveryStatus(@NonNull String alias, int status)
throws InternalRecoveryServiceException {
try {
mBinder.setRecoveryStatus(alias, status);
@@ -390,7 +391,7 @@
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+ public int getRecoveryStatus(@NonNull String alias) throws InternalRecoveryServiceException {
try {
Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
Integer status = allStatuses.get(alias);
@@ -410,8 +411,7 @@
* Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
* is necessary to recover data.
*
- * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} or {@link
- * KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}
+ * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN}
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
@@ -450,51 +450,6 @@
}
/**
- * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
- * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
- * called.
- *
- * @return list of recovery secret types
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
- */
- @NonNull
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
- throws InternalRecoveryServiceException {
- try {
- return mBinder.getPendingRecoverySecretTypes();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw wrapUnexpectedServiceSpecificException(e);
- }
- }
-
- /**
- * Method notifies KeyStore that a user-generated secret is available. This method generates a
- * symmetric session key which a trusted remote device can use to return a recovery key. Caller
- * should use {@link KeyChainProtectionParams#clearSecret} to override the secret value in
- * memory.
- *
- * @param recoverySecret user generated secret together with parameters necessary to regenerate
- * it on a new device.
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
- */
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
- throws InternalRecoveryServiceException {
- try {
- mBinder.recoverySecretAvailable(recoverySecret);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw wrapUnexpectedServiceSpecificException(e);
- }
- }
-
- /**
* Deprecated.
* Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
* key store. Returns the raw material of the key.
@@ -651,7 +606,7 @@
* <p>A recovery session is required to restore keys from a remote store.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public RecoverySession createRecoverySession() {
+ public @NonNull RecoverySession createRecoverySession() {
return RecoverySession.newInstance(this);
}
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 208b9b2..cf8a9dd 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -49,7 +49,8 @@
private final String mSessionId;
private final RecoveryController mRecoveryController;
- private RecoverySession(RecoveryController recoveryController, String sessionId) {
+ private RecoverySession(@NonNull RecoveryController recoveryController,
+ @NonNull String sessionId) {
mRecoveryController = recoveryController;
mSessionId = sessionId;
}
@@ -58,14 +59,14 @@
* A new session, started by the {@link RecoveryController}.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- static RecoverySession newInstance(RecoveryController recoveryController) {
+ static @NonNull RecoverySession newInstance(RecoveryController recoveryController) {
return new RecoverySession(recoveryController, newSessionId());
}
/**
* Returns a new random session ID.
*/
- private static String newSessionId() {
+ private static @NonNull String newSessionId() {
SecureRandom secureRandom = new SecureRandom();
byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
secureRandom.nextBytes(sessionId);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 0f7cea2..60537a4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -527,6 +527,9 @@
* <pre> <autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
* <compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/>
* </autofill-service></pre>
+ *
+ * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
+ * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index f32dee1..ccec483 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -149,24 +149,33 @@
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder builder = new StringBuilder("Dataset[id=");
+ final StringBuilder builder = new StringBuilder("Dataset[");
if (mId == null) {
- builder.append("null");
+ builder.append("noId");
} else {
// Cannot disclose id because it could contain PII.
- builder.append(mId.length()).append("_chars");
+ builder.append("id=").append(mId.length()).append("_chars");
}
+ if (mFieldIds != null) {
+ builder.append(", fieldIds=").append(mFieldIds);
+ }
+ if (mFieldValues != null) {
+ builder.append(", fieldValues=").append(mFieldValues);
+ }
+ if (mFieldPresentations != null) {
+ builder.append(", fieldPresentations=").append(mFieldPresentations.size());
- return builder
- .append(", fieldIds=").append(mFieldIds)
- .append(", fieldValues=").append(mFieldValues)
- .append(", fieldPresentations=")
- .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
- .append(", fieldFilters=")
- .append(mFieldFilters == null ? 0 : mFieldFilters.size())
- .append(", hasPresentation=").append(mPresentation != null)
- .append(", hasAuthentication=").append(mAuthentication != null)
- .append(']').toString();
+ }
+ if (mFieldFilters != null) {
+ builder.append(", fieldFilters=").append(mFieldFilters.size());
+ }
+ if (mPresentation != null) {
+ builder.append(", hasPresentation");
+ }
+ if (mAuthentication != null) {
+ builder.append(", hasAuthentication");
+ }
+ return builder.append(']').toString();
}
/**
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index be84ba9..2bc4b8f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -557,23 +557,40 @@
if (!sDebug) return super.toString();
// TODO: create a dump() method instead
- return new StringBuilder(
- "FillResponse : [mRequestId=" + mRequestId)
- .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
- .append(", saveInfo=").append(mSaveInfo)
- .append(", clientState=").append(mClientState != null)
- .append(", hasPresentation=").append(mPresentation != null)
- .append(", hasHeader=").append(mHeader != null)
- .append(", hasFooter=").append(mFooter != null)
- .append(", hasAuthentication=").append(mAuthentication != null)
- .append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
- .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
- .append(", disableDuration=").append(mDisableDuration)
- .append(", flags=").append(mFlags)
- .append(", fieldClassificationIds=")
- .append(Arrays.toString(mFieldClassificationIds))
- .append("]")
- .toString();
+ final StringBuilder builder = new StringBuilder(
+ "FillResponse : [mRequestId=" + mRequestId);
+ if (mDatasets != null) {
+ builder.append(", datasets=").append(mDatasets.getList());
+ }
+ if (mSaveInfo != null) {
+ builder.append(", saveInfo=").append(mSaveInfo);
+ }
+ if (mClientState != null) {
+ builder.append(", hasClientState");
+ }
+ if (mPresentation != null) {
+ builder.append(", hasPresentation");
+ }
+ if (mHeader != null) {
+ builder.append(", hasHeader");
+ }
+ if (mFooter != null) {
+ builder.append(", hasFooter");
+ }
+ if (mAuthentication != null) {
+ builder.append(", hasAuthentication");
+ }
+ if (mAuthenticationIds != null) {
+ builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
+ }
+ builder.append(", disableDuration=").append(mDisableDuration);
+ if (mFlags != 0) {
+ builder.append(", flags=").append(mFlags);
+ }
+ if (mFieldClassificationIds != null) {
+ builder.append(Arrays.toString(mFieldClassificationIds));
+ }
+ return builder.append("]").toString();
}
/////////////////////////////////////
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 1be1be6..4943fc8 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -689,22 +689,37 @@
public String toString() {
if (!sDebug) return super.toString();
- return new StringBuilder("SaveInfo: [type=")
+ final StringBuilder builder = new StringBuilder("SaveInfo: [type=")
.append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
.append(", requiredIds=").append(Arrays.toString(mRequiredIds))
- .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
- .append(", description=").append(mDescription)
- .append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
- mNegativeButtonStyle))
- .append(", flags=").append(mFlags)
- .append(", customDescription=").append(mCustomDescription)
- .append(", validator=").append(mValidator)
- .append(", sanitizerKeys=")
- .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
- .append(", sanitizerValues=")
- .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
- .append(", triggerId=").append(mTriggerId)
- .append("]").toString();
+ .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class,
+ "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle));
+ if (mOptionalIds != null) {
+ builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds));
+ }
+ if (mDescription != null) {
+ builder.append(", description=").append(mDescription);
+ }
+ if (mFlags != 0) {
+ builder.append(", flags=").append(mFlags);
+ }
+ if (mCustomDescription != null) {
+ builder.append(", customDescription=").append(mCustomDescription);
+ }
+ if (mValidator != null) {
+ builder.append(", validator=").append(mValidator);
+ }
+ if (mSanitizerKeys != null) {
+ builder.append(", sanitizerKeys=").append(mSanitizerKeys.length);
+ }
+ if (mSanitizerValues != null) {
+ builder.append(", sanitizerValues=").append(mSanitizerValues.length);
+ }
+ if (mTriggerId != null) {
+ builder.append(", triggerId=").append(mTriggerId);
+ }
+
+ return builder.append("]").toString();
}
/////////////////////////////////////
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 18431ca..febca7e 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -704,7 +704,12 @@
// Spans other than ReplacementSpan can be ignored because line top and bottom are
// disjunction of all tops and bottoms, although it's not optimal.
final Paint paint = getPaint();
- paint.getTextBounds(text, start, end, mTempRect);
+ if (text instanceof PrecomputedText) {
+ PrecomputedText precomputed = (PrecomputedText) text;
+ precomputed.getBounds(start, end, mTempRect);
+ } else {
+ paint.getTextBounds(text, start, end, mTempRect);
+ }
final Paint.FontMetricsInt fm = paint.getFontMetricsInt();
return mTempRect.top < fm.top || mTempRect.bottom > fm.bottom;
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 980f4704..a107cab 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -297,6 +298,18 @@
}
/**
+ * Retrieves the bounding rectangle that encloses all of the characters, with an implied origin
+ * at (0, 0).
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull Rect bounds) {
+ nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
+ paint.getBidiFlags(), bounds);
+ }
+
+ /**
* Generates new MeasuredParagraph for Bidi computation.
*
* If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -728,4 +741,7 @@
@CriticalNative
private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
+ int end, int bidiFlag, Rect rect);
}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 9458184..413df05 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,6 +19,8 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.text.style.MetricAffectingSpan;
import com.android.internal.util.Preconditions;
@@ -57,10 +59,12 @@
* </pre>
*
* Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
+ * {@link android.widget.TextView} will be rejected.
+ *
+ * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
+ * PrecomputedText.
*/
-public class PrecomputedText implements Spanned {
+public class PrecomputedText implements Spannable {
private static final char LINE_FEED = '\n';
/**
@@ -283,7 +287,7 @@
// The original text.
- private final @NonNull SpannedString mText;
+ private final @NonNull SpannableString mText;
// The inclusive start offset of the measuring target.
private final @IntRange(from = 0) int mStart;
@@ -304,6 +308,9 @@
* presented can save work on the UI thread.
* </p>
*
+ * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the
+ * created PrecomputedText.
+ *
* @param text the text to be measured
* @param params parameters that define how text will be precomputed
* @return A {@link PrecomputedText}
@@ -347,7 +354,7 @@
private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
@IntRange(from = 0) int end, @NonNull Params params,
@NonNull ParagraphInfo[] paraInfo) {
- mText = new SpannedString(text);
+ mText = new SpannableString(text, true /* ignoreNoCopySpan */);
mStart = start;
mEnd = end;
mParams = params;
@@ -457,6 +464,21 @@
return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
}
+ /** @hide */
+ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @NonNull Rect bounds) {
+ final int paraIndex = findParaIndex(start);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ if (start < paraStart || paraEnd < end) {
+ throw new RuntimeException("Cannot measured across the paragraph:"
+ + "para: (" + paraStart + ", " + paraEnd + "), "
+ + "request: (" + start + ", " + end + ")");
+ }
+ getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
+ start - paraStart, end - paraStart, bounds);
+ }
+
/**
* Returns the size of native PrecomputedText memory usage.
*
@@ -472,6 +494,35 @@
}
///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spannable overrides
+ //
+ // Do not allow to modify MetricAffectingSpan
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void setSpan(Object what, int start, int end, int flags) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be set to PrecomputedText.");
+ }
+ mText.setSpan(what, start, end, flags);
+ }
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void removeSpan(Object what) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be removed from PrecomputedText.");
+ }
+ mText.removeSpan(what);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
// Spanned overrides
//
// Just proxy for underlying mText if appropriate.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 69938cb..abc19d0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2882,9 +2882,8 @@
/**
* @hide
*/
- public String toString(String prefix) {
- StringBuilder sb = new StringBuilder(256);
- sb.append("{(");
+ public void dumpDimensions(StringBuilder sb) {
+ sb.append('(');
sb.append(x);
sb.append(',');
sb.append(y);
@@ -2895,6 +2894,15 @@
sb.append((height == MATCH_PARENT ? "fill" : (height == WRAP_CONTENT
? "wrap" : String.valueOf(height))));
sb.append(")");
+ }
+
+ /**
+ * @hide
+ */
+ public String toString(String prefix) {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append('{');
+ dumpDimensions(sb);
if (horizontalMargin != 0) {
sb.append(" hm=");
sb.append(horizontalMargin);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index e2601dc..df04beb 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -215,6 +215,10 @@
mWindow.destroy();
mWindow = null;
}
+ mPrevPosInView.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevPosInView.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevStartCoordsInSurface.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevStartCoordsInSurface.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
}
}
@@ -321,19 +325,24 @@
// Clamp copy coordinates inside the surface to avoid displaying distorted content.
final int clampedStartXInSurface = Math.max(0,
- Math.min(startXInSurface, surfaceWidth - mWindowWidth));
+ Math.min(startXInSurface, surfaceWidth - mBitmapWidth));
final int clampedStartYInSurface = Math.max(0,
- Math.min(startYInSurface, surfaceHeight - mWindowHeight));
+ Math.min(startYInSurface, surfaceHeight - mBitmapHeight));
+
+ // Clamp window coordinates inside the parent surface, to avoid displaying
+ // the magnifier out of screen or overlapping with system insets.
+ final Rect insets = mView.getRootWindowInsets().getSystemWindowInsets();
+ final int windowCoordsX = Math.max(insets.left,
+ Math.min(surfaceWidth - mWindowWidth - insets.right, mWindowCoords.x));
+ final int windowCoordsY = Math.max(insets.top,
+ Math.min(surfaceHeight - mWindowHeight - insets.bottom, mWindowCoords.y));
// Perform the pixel copy.
mPixelCopyRequestRect.set(clampedStartXInSurface,
clampedStartYInSurface,
clampedStartXInSurface + mBitmapWidth,
clampedStartYInSurface + mBitmapHeight);
- final int windowCoordsX = mWindowCoords.x;
- final int windowCoordsY = mWindowCoords.y;
final InternalPopupWindow currentWindowInstance = mWindow;
-
final Bitmap bitmap =
Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index dab0f73..f52854a 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -33,6 +33,7 @@
// TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden.
/**
+ * @hide
* A View that contains the controls for MediaPlayer2.
* It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
* "Subtitle", "Full Screen", and it is also possible to add multiple custom buttons.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f21c0b9..c366a91 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5640,6 +5640,8 @@
needEditableForNotification = true;
}
+ PrecomputedText precomputed =
+ (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
if (type == BufferType.EDITABLE || getKeyListener() != null
|| needEditableForNotification) {
createEditorIfNeeded();
@@ -5649,10 +5651,7 @@
setFilters(t, mFilters);
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
- } else if (type == BufferType.SPANNABLE || mMovement != null) {
- text = mSpannableFactory.newSpannable(text);
- } else if (text instanceof PrecomputedText) {
- PrecomputedText precomputed = (PrecomputedText) text;
+ } else if (precomputed != null) {
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
@@ -5665,6 +5664,8 @@
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
}
+ } else if (type == BufferType.SPANNABLE || mMovement != null) {
+ text = mSpannableFactory.newSpannable(text);
} else if (!(text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 214ff3a..388eae2 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -47,6 +47,7 @@
// TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted.
/**
+ * @hide
* Displays a video file. VideoView2 class is a View class which is wrapping {@link MediaPlayer2}
* so that developers can easily implement a video rendering application.
*
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 4901080..f0e7796 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -115,6 +115,14 @@
command.append(' ');
command.append(appProcess);
+ // Generate bare minimum of debug information to be able to backtrace through JITed code.
+ // We assume that if the invoke wrapper is used, backtraces are desirable:
+ // * The wrap.sh script can only be used by debuggable apps, which would enable this flag
+ // without the script anyway (the fork-zygote path). So this makes the two consistent.
+ // * The wrap.* property can only be used on userdebug builds and is likely to be used by
+ // developers (e.g. enable debug-malloc), in which case backtraces are also useful.
+ command.append(" -Xcompiler-option --generate-mini-debug-info");
+
command.append(" /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 933cc7a..577fa17 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -443,7 +443,7 @@
*/
public static int resolveColor(Context context, int color) {
if (color == Notification.COLOR_DEFAULT) {
- return context.getColor(com.android.internal.R.color.notification_icon_default_color);
+ return context.getColor(com.android.internal.R.color.notification_default_color_light);
}
return color;
}
@@ -475,20 +475,15 @@
int backgroundColor, boolean isDark) {
final int resolvedColor = resolveColor(context, notificationColor);
- final int actionBg = context.getColor(
- com.android.internal.R.color.notification_action_list);
-
int color = resolvedColor;
- color = NotificationColorUtil.ensureLargeTextContrast(color, actionBg, isDark);
color = NotificationColorUtil.ensureTextContrast(color, backgroundColor, isDark);
if (color != resolvedColor) {
if (DEBUG){
Log.w(TAG, String.format(
- "Enhanced contrast of notification for %s %s (over action)"
+ "Enhanced contrast of notification for %s"
+ " and %s (over background) by changing #%s to %s",
context.getPackageName(),
- NotificationColorUtil.contrastChange(resolvedColor, color, actionBg),
NotificationColorUtil.contrastChange(resolvedColor, color, backgroundColor),
Integer.toHexString(resolvedColor), Integer.toHexString(color)));
}
@@ -552,6 +547,17 @@
}
}
+ public static int resolveDefaultColor(Context context, int backgroundColor) {
+ boolean useDark = shouldUseDark(backgroundColor);
+ if (useDark) {
+ return context.getColor(
+ com.android.internal.R.color.notification_default_color_light);
+ } else {
+ return context.getColor(
+ com.android.internal.R.color.notification_default_color_dark);
+ }
+ }
+
public static int resolveActionBarColor(Context context, int backgroundColor) {
if (backgroundColor == Notification.COLOR_DEFAULT) {
return context.getColor(com.android.internal.R.color.notification_action_list);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index bec70fd..bff34ca 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -74,7 +74,6 @@
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
- void recoverySecretAvailable(in KeyChainProtectionParams recoverySecret);
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
in List<KeyChainProtectionParams> secrets);
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 0017f6c..c9bfa13 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -18,6 +18,7 @@
#include "GraphicsJNI.h"
#include "core_jni_helpers.h"
+#include <android/api-level.h>
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
@@ -467,6 +468,13 @@
static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
jint meshWidth, jint meshHeight, jfloatArray jverts,
jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+ if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
+ // Before P we forgot to respect these. Now that we do respect them, explicitly
+ // zero them for backward compatibility.
+ vertIndex = 0;
+ colorIndex = 0;
+ }
+
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
@@ -474,7 +482,8 @@
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
- vertA.ptr(), colorA.ptr(), paint);
+ vertA.ptr() + vertIndex*2,
+ colorA.ptr() + colorIndex, paint);
}
static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index d33337d..9d79417 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "MeasuredParagraph"
+#include "GraphicsJNI.h"
#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
@@ -109,6 +110,33 @@
return r;
}
+// Regular JNI
+static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jlong paintPtr,
+ jint start, jint end, jint bidiFlags, jobject bounds) {
+ ScopedCharArrayRO text(env, javaText);
+ const minikin::U16StringPiece textBuffer(text.get(), text.size());
+
+ minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
+ Paint* paint = toPaint(paintPtr);
+ const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
+ minikin::Layout layout = MinikinUtils::doLayout(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface, textBuffer.data(), start, end - start,
+ textBuffer.size(), mt);
+
+ minikin::MinikinRect rect;
+ layout.getBounds(&rect);
+
+ SkRect r;
+ r.fLeft = rect.mLeft;
+ r.fTop = rect.mTop;
+ r.fRight = rect.mRight;
+ r.fBottom = rect.mBottom;
+
+ SkIRect ir;
+ r.roundOut(&ir);
+ GraphicsJNI::irect_to_jrect(ir, env, bounds);
+}
+
// CriticalNative
static jlong nGetReleaseFunc() {
return toJLong(&releaseMeasuredParagraph);
@@ -128,6 +156,7 @@
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
+ {"nGetBounds", "(J[CJIIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
};
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 953da17..89665db 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -228,6 +228,7 @@
optional SettingProto ble_scan_low_power_interval_ms = 161 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto ble_scan_balanced_interval_ms = 162 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto ble_scan_low_latency_interval_ms = 163 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto ble_scan_background_mode = 389 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_saved_state = 164 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_supplicant_scan_interval_ms = 165 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_enhanced_auto_join = 166 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -507,7 +508,7 @@
optional SettingsProto backup_agent_timeout_parameters = 386;
// Please insert fields in the same order as in
// frameworks/base/core/java/android/provider/Settings.java.
- // Next tag = 389;
+ // Next tag = 390;
}
message SecureSettingsProto {
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 722102e..449d3e7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,13 +130,14 @@
<drawable name="notification_template_divider_media">#29ffffff</drawable>
<color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color>
<color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color>
- <color name="notification_secondary_text_color_light">@color/secondary_text_material_light</color>
- <color name="notification_secondary_text_color_dark">@color/secondary_text_material_dark</color>
+ <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color>
+ <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color>
+ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
+ <color name="notification_default_color_light">#a3202124</color>
<color name="notification_material_background_color">#ffffffff</color>
<color name="notification_default_color">#757575</color> <!-- Gray 600 -->
- <color name="notification_icon_default_color">@color/notification_default_color</color>
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ba85f70..7038887 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -159,6 +159,9 @@
<!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
<string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
+ <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. -->
+ <string name="RestrictedStateContentMsimTemplate">Temporarily turned off by your carrier for SIM <xliff:g id="simNumber" example="1">%d</xliff:g></string>
+
<!-- Displayed to tell the user that they should switch their network preference. -->
<string name="NetworkPreferenceSwitchTitle">Can\u2019t reach mobile network</string>
<!-- Displayed to tell the user that they should switch their network preference. -->
@@ -227,14 +230,14 @@
<string name="roamingTextSearching">Searching for Service</string>
<!-- Displayed when WFC registration fails -->
- <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
+ <string name="wfcRegErrorTitle">Couldn\u2019t set up Wi\u2011Fi calling</string>
<!-- WFC Operator Error Messages showed as alerts -->
<string-array name="wfcOperatorErrorAlertMessages">
<item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
</string-array>
<!-- WFC Operator Error Messages showed as notifications -->
<string-array name="wfcOperatorErrorNotificationMessages">
- <item>Register with your carrier (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
+ <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
</string-array>
<!-- Template for showing mobile network operator name while WFC is active -->
<string-array name="wfcSpnFormats">
@@ -3085,13 +3088,13 @@
<string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
<!-- Notification title for a nearby carrier wireless network.-->
<string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
- <!-- Notification title when the system is connecting to the specified open network. The network name is specified in the notification content. -->
- <string name="wifi_available_title_connecting">Connecting to open Wi\u2011Fi network</string>
- <!-- Notification title when the system has connected to the open network. The network name is specified in the notification content. -->
+ <!-- Notification title when the system is connecting to the specified network. The network name is specified in the notification content. -->
+ <string name="wifi_available_title_connecting">Connecting to Wi\u2011Fi network</string>
+ <!-- Notification title when the system has connected to the network. The network name is specified in the notification content. -->
<string name="wifi_available_title_connected">Connected to Wi\u2011Fi network</string>
- <!-- Notification title when the system failed to connect to the specified open network. -->
+ <!-- Notification title when the system failed to connect to the specified network. -->
<string name="wifi_available_title_failed_to_connect">Could not connect to Wi\u2011Fi network</string>
- <!-- Notification content when the system failed to connect to the specified open network. This informs the user that tapping on this notification will open the wifi picker. -->
+ <!-- Notification content when the system failed to connect to the specified network. This informs the user that tapping on this notification will open the wifi picker. -->
<string name="wifi_available_content_failed_to_connect">Tap to see all networks</string>
<!-- Notification action name for connecting to the network specified in the notification body. -->
<string name="wifi_available_action_connect">Connect</string>
@@ -4833,6 +4836,12 @@
<string name="mmcc_illegal_ms">SIM not allowed for voice</string>
<string name="mmcc_illegal_me">Phone not allowed for voice</string>
+ <!-- Title of notification when UE fails to register network with MM reject cause code when multiple SIMs are active. -->
+ <string name="mmcc_authentication_reject_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+ <string name="mmcc_imsi_unknown_in_hlr_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not provisioned</string>
+ <string name="mmcc_illegal_ms_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+ <string name="mmcc_illegal_me_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+
<!-- Popup window default title to be read by a screen reader-->
<string name="popup_window_default_title">Popup Window</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index fa3cf2f..b8a046f 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -473,7 +473,7 @@
<style name="TextAppearance.Material.Notification.Reply" />
<style name="TextAppearance.Material.Notification.Title">
- <item name="textColor">@color/notification_primary_text_color_light</item>
+ <item name="fontFamily">sans-serif-medium</item>
<item name="textSize">@dimen/notification_title_text_size</item>
</style>
@@ -482,7 +482,6 @@
</style>
<style name="TextAppearance.Material.Notification.Info">
- <item name="textColor">@color/notification_secondary_text_color_light</item>
<item name="textSize">@dimen/notification_subtext_size</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9d65a60..3bf97d0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -579,6 +579,7 @@
<java-symbol type="string" name="RestrictedOnEmergencyTitle" />
<java-symbol type="string" name="RestrictedOnNormalTitle" />
<java-symbol type="string" name="RestrictedStateContent" />
+ <java-symbol type="string" name="RestrictedStateContentMsimTemplate" />
<java-symbol type="string" name="notification_channel_network_alert" />
<java-symbol type="string" name="notification_channel_call_forward" />
<java-symbol type="string" name="notification_channel_emergency_callback" />
@@ -2031,6 +2032,10 @@
<java-symbol type="string" name="mmcc_imsi_unknown_in_hlr" />
<java-symbol type="string" name="mmcc_illegal_ms" />
<java-symbol type="string" name="mmcc_illegal_me" />
+ <java-symbol type="string" name="mmcc_authentication_reject_msim_template" />
+ <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr_msim_template" />
+ <java-symbol type="string" name="mmcc_illegal_ms_msim_template" />
+ <java-symbol type="string" name="mmcc_illegal_me_msim_template" />
<java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="vr_listener_binding_label" />
<java-symbol type="string" name="condition_provider_service_binding_label" />
@@ -2117,7 +2122,6 @@
<java-symbol type="layout" name="notification_template_material_big_text" />
<java-symbol type="layout" name="notification_template_header" />
<java-symbol type="layout" name="notification_material_media_action" />
- <java-symbol type="color" name="notification_icon_default_color" />
<java-symbol type="color" name="notification_progress_background_color" />
<java-symbol type="id" name="media_actions" />
@@ -2967,6 +2971,8 @@
<java-symbol type="color" name="notification_primary_text_color_dark" />
<java-symbol type="color" name="notification_secondary_text_color_light" />
<java-symbol type="color" name="notification_secondary_text_color_dark" />
+ <java-symbol type="color" name="notification_default_color_light" />
+ <java-symbol type="color" name="notification_default_color_dark" />
<java-symbol type="string" name="app_category_game" />
<java-symbol type="string" name="app_category_audio" />
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 374d0d0..3c1526b 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 6fe19a2..c88a722 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -16,6 +16,7 @@
package android.privacy;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -72,27 +73,27 @@
final LongitudinalReportingEncoder encoder =
LongitudinalReportingEncoder.createInsecureEncoderForTest(
config);
- assertEquals(0, encoder.encodeBoolean(true)[0]);
- assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
- assertEquals(1, encoder.encodeBoolean(true)[0]);
- assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
// Test if IRR returns original result when f = 0
final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
@@ -128,6 +129,31 @@
}
@Test
+ public void testLongitudinalReportingInsecureEncoder_setSeedCorrectly() throws Exception {
+ final int n = 10000;
+ final double f = 0.35;
+ final double expectedTrueSum = n * f;
+ final double valueRange = 5 * Math.sqrt(n * f * (1 - f));
+ int trueSum = 0;
+ for (int i = 0; i < n; i++) {
+ final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
+ "encoder" + i, // encoderId
+ f, // probabilityF
+ 0, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder
+ = LongitudinalReportingEncoder.createInsecureEncoderForTest(config);
+ boolean encodedFalse = encoder.encodeBoolean(false)[0] > 0;
+ if (encodedFalse) {
+ trueSum += 1;
+ }
+ }
+ // Total number of true(s) should be around the mean (10000 * 0.35)
+ assertThat((double) trueSum).isLessThan(expectedTrueSum + valueRange);
+ assertThat((double) trueSum).isAtLeast(expectedTrueSum - valueRange);
+ }
+
+ @Test
public void testLongitudinalReportingEncoder_basicPRRTest() throws Exception {
// Should always return original value when p = 0
for (int i = 0; i < 10; i++) {
@@ -290,8 +316,8 @@
}
}
// Total number of true(s) should be around the mean (1000 * 0.8)
- assertTrue(trueSum1 < expectedTrueSum1 + valueRange1);
- assertTrue(trueSum1 > expectedTrueSum1 - valueRange1);
+ assertThat((double) trueSum1).isLessThan(expectedTrueSum1 + valueRange1);
+ assertThat((double) trueSum1).isAtLeast(expectedTrueSum1 - valueRange1);
// Confirm if PRR randomizer is working correctly
final int n2 = 1000;
@@ -314,8 +340,8 @@
}
}
// Total number of true(s) should be around the mean (1000 * 0.2)
- assertTrue(trueSum2 < expectedTrueSum2 + valueRange2);
- assertTrue(trueSum2 > expectedTrueSum2 - valueRange2);
+ assertThat((double) trueSum2).isLessThan(expectedTrueSum2 + valueRange2);
+ assertThat((double) trueSum2).isAtLeast(expectedTrueSum2 - valueRange2);
}
@Test
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index fa0343d..71bd8f1 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -79,11 +79,11 @@
public void testRapporEncoder_IRRWithPRR() throws Exception {
int numBits = 8;
final long inputValue = 254L;
- final long prrValue = 250L;
- final long prrAndIrrValue = 244L;
+ final long expectedPrrValue = 126L;
+ final long expectedPrrAndIrrValue = 79L;
final RapporConfig config1 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0, // probabilityP
@@ -93,12 +93,12 @@
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder1 = RapporEncoder.createInsecureEncoderForTest(config1);
// Verify that PRR is working as expected.
- assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
+ assertEquals(expectedPrrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
assertTrue(encoder1.isInsecureEncoderForTest());
// Verify that IRR is working as expected.
final RapporConfig config2 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0, // probabilityF
0.3, // probabilityP
@@ -107,11 +107,12 @@
2); // numBloomHashes
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder2 = RapporEncoder.createInsecureEncoderForTest(config2);
- assertEquals(prrAndIrrValue, toLong(encoder2.encodeBits(toBytes(prrValue))));
+ assertEquals(expectedPrrAndIrrValue,
+ toLong(encoder2.encodeBits(toBytes(expectedPrrValue))));
// Test that end-to-end is the result of PRR + IRR.
final RapporConfig config3 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0.3, // probabilityP
@@ -120,7 +121,7 @@
2); // numBloomHashes
final RapporEncoder encoder3 = RapporEncoder.createInsecureEncoderForTest(config3);
// Verify that PRR is working as expected.
- assertEquals(prrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
+ assertEquals(expectedPrrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
}
@Test
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d925441..b0bc102 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1490,6 +1490,10 @@
* across the top of the bitmap from left to right. A more general version of this method is
* drawVertices().
*
+ * Prior to API level {@value Build.VERSION_CODES#P} vertOffset and colorOffset were ignored,
+ * effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
+ * these parameters will be respected.
+ *
* @param bitmap The bitmap to draw using the mesh
* @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
* @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d04bb2e..f341cf9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -276,6 +276,8 @@
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
const Typeface* typeface);
+ static int GetApiLevel() { return sApiLevel; }
+
protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9db39d9..0cd1c15 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -342,7 +342,8 @@
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
- if (!opaque) {
+ // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
+ if (!opaque || wideColorGamut) {
canvas->clear(SK_ColorTRANSPARENT);
}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 412cc29..2152e1e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -83,6 +83,20 @@
*/
public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
+ /**
+ * GNSS measurement tracking loop state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_CODE_LOCK, STATE_BIT_SYNC, STATE_SUBFRAME_SYNC,
+ STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
+ STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
+ STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
+ STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
/** This GNSS measurement's tracking state is invalid or unknown. */
public static final int STATE_UNKNOWN = 0;
/** This GNSS measurement's tracking state has code lock. */
@@ -133,29 +147,68 @@
private static final int STATE_ALL = 0x3fff; // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
/**
- * The state of the 'Accumulated Delta Range' is invalid or unknown.
+ * GNSS measurement accumulated delta range state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
+ ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
+ ADR_STATE_HALF_CYCLE_REPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AdrState {}
+
+ /**
+ * The state of the value {@link #getAccumulatedDeltaRangeMeters()} is invalid or unknown.
*/
public static final int ADR_STATE_UNKNOWN = 0;
/**
- * The state of the 'Accumulated Delta Range' is valid.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} is valid.
*/
public static final int ADR_STATE_VALID = (1<<0);
/**
- * The state of the 'Accumulated Delta Range' has detected a reset.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has detected a reset.
*/
public static final int ADR_STATE_RESET = (1<<1);
/**
- * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has a cycle slip detected.
*/
public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
/**
- * All the 'Accumulated Delta Range' flags.
+ * Reports whether the value {@link #getAccumulatedDeltaRangeMeters()} has resolved the half
+ * cycle ambiguity.
+ *
+ * <p> When this bit is set, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier full cycles.
+ *
+ * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier half cycles.
*/
- private static final int ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+ public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
+
+ /**
+ * Reports whether the flag {@link #ADR_STATE_HALF_CYCLE_RESOLVED} has been reported by the
+ * GNSS hardware.
+ *
+ * <p> When this bit is set, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * can be low (centimeter level) whether or not the half cycle ambiguity is resolved.
+ *
+ * <p> When this bit is unset, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * is larger, to cover the potential error due to half cycle ambiguity being unresolved.
+ */
+ public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
+ /**
+ * All the 'Accumulated Delta Range' flags.
+ * @hide
+ */
+ @TestApi
+ public static final int ADR_STATE_ALL =
+ ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP |
+ ADR_STATE_HALF_CYCLE_RESOLVED | ADR_STATE_HALF_CYCLE_REPORTED;
// End enumerations in sync with gps.h
@@ -278,6 +331,7 @@
*
* <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
*/
+ @State
public int getState() {
return mState;
}
@@ -287,7 +341,7 @@
* @hide
*/
@TestApi
- public void setState(int value) {
+ public void setState(@State int value) {
mState = value;
}
@@ -557,6 +611,7 @@
* <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
* cycle slip (indicating 'loss of lock').
*/
+ @AdrState
public int getAccumulatedDeltaRangeState() {
return mAccumulatedDeltaRangeState;
}
@@ -566,7 +621,7 @@
* @hide
*/
@TestApi
- public void setAccumulatedDeltaRangeState(int value) {
+ public void setAccumulatedDeltaRangeState(@AdrState int value) {
mAccumulatedDeltaRangeState = value;
}
@@ -589,7 +644,15 @@
if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
builder.append("CycleSlip|");
}
- int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_RESOLVED) ==
+ ADR_STATE_HALF_CYCLE_RESOLVED) {
+ builder.append("HalfCycleResolved|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_REPORTED)
+ == ADR_STATE_HALF_CYCLE_REPORTED) {
+ builder.append("HalfCycleReported|");
+ }
+ int remainingStates = mAccumulatedDeltaRangeState & ~ADR_STATE_ALL;
if (remainingStates > 0) {
builder.append("Other(");
builder.append(Integer.toBinaryString(remainingStates));
@@ -674,7 +737,7 @@
* L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
* common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
*
- * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+ * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
* measurement objects will be reported for this same satellite, in one of the measurement
* objects, all the values related to L1 will be filled, and in the other all of the values
* related to L5 will be filled.
@@ -709,7 +772,10 @@
/**
* Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierCycles() {
return isFlagSet(HAS_CARRIER_CYCLES);
}
@@ -720,16 +786,24 @@
* <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
*
* <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
*/
+ @Deprecated
public long getCarrierCycles() {
return mCarrierCycles;
}
/**
* Sets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierCycles(long value) {
setFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = value;
@@ -737,9 +811,13 @@
/**
* Resets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierCycles() {
resetFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = Long.MIN_VALUE;
@@ -747,7 +825,10 @@
/**
* Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierPhase() {
return isFlagSet(HAS_CARRIER_PHASE);
}
@@ -764,16 +845,24 @@
* <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
*
* <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
*/
+ @Deprecated
public double getCarrierPhase() {
return mCarrierPhase;
}
/**
* Sets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierPhase(double value) {
setFlag(HAS_CARRIER_PHASE);
mCarrierPhase = value;
@@ -781,9 +870,14 @@
/**
* Resets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierPhase() {
resetFlag(HAS_CARRIER_PHASE);
mCarrierPhase = Double.NaN;
@@ -792,7 +886,10 @@
/**
* Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
* otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierPhaseUncertainty() {
return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
}
@@ -803,16 +900,24 @@
* <p>The uncertainty is represented as an absolute (single sided) value.
*
* <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeUncertaintyMeters()} instead.
*/
+ @Deprecated
public double getCarrierPhaseUncertainty() {
return mCarrierPhaseUncertainty;
}
/**
* Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierPhaseUncertainty(double value) {
setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = value;
@@ -820,9 +925,14 @@
/**
* Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierPhaseUncertainty() {
resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = Double.NaN;
@@ -859,7 +969,7 @@
case MULTIPATH_INDICATOR_NOT_DETECTED:
return "NotDetected";
default:
- return "<Invalid:" + mMultipathIndicator + ">";
+ return "<Invalid: " + mMultipathIndicator + ">";
}
}
@@ -916,11 +1026,12 @@
* number. Hence in cases of strong jamming, in the band of this signal, this value will go more
* negative.
*
- * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
* components) may also affect the typical output of of this value on any given hardware design
* in an open sky test - the important aspect of this output is that changes in this value are
* indicative of changes on input signal power in the frequency band for this measurement.
- * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+ *
+ * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
*/
public double getAutomaticGainControlLevelDb() {
return mAutomaticGainControlLevelInDb;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1536bb6..e408a11 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1352,6 +1352,7 @@
//====================================================================
// Offload query
/**
+ * @hide
* Returns whether offloaded playback of an audio format is supported on the device.
* Offloaded playback is where the decoding of an audio stream is not competing with other
* software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3885f90..acab8bb 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -747,7 +747,8 @@
public static final int FOR_SYSTEM = 4;
public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
public static final int FOR_ENCODED_SURROUND = 6;
- private static final int NUM_FORCE_USE = 7;
+ public static final int FOR_VIBRATE_RINGING = 7;
+ private static final int NUM_FORCE_USE = 8;
public static String forceUseUsageToString(int usage) {
switch (usage) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9c48e09..87b5d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -896,6 +896,7 @@
}
/**
+ * @hide
* Sets whether this track will play through the offloaded audio path.
* When set to true, at build time, the audio format will be checked against
* {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
@@ -2979,6 +2980,7 @@
}
/**
+ * @hide
* Abstract class to receive event notification about the stream playback.
* See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
* the callback on the given {@link AudioTrack} instance.
@@ -3012,6 +3014,7 @@
private final Object mStreamEventCbLock = new Object();
/**
+ * @hide
* Sets the callback for the notification of stream events.
* @param executor {@link Executor} to handle the callbacks
* @param eventCallback the callback to receive the stream event notifications
@@ -3031,6 +3034,7 @@
}
/**
+ * @hide
* Unregisters the callback for notification of stream events, previously set
* by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
*/
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 6d58a94..a53fa11 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -40,6 +40,7 @@
import java.util.Map;
/**
+ * @hide
* Structure for data source descriptor.
*
* Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/Media2DataSource.java
index 8ee4a70..08df632 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/Media2DataSource.java
@@ -21,6 +21,7 @@
import java.io.IOException;
/**
+ * @hide
* For supplying media data to the framework. Implement this if your app has
* special requirements for the way media data is obtained.
*
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index f246005..452371a 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -30,6 +30,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Browses media content offered by a {@link MediaLibraryService2}.
*/
public class MediaBrowser2 extends MediaController2 {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index ba9056e..e3fba0c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2359,17 +2359,61 @@
public static final int CRYPTO_MODE_AES_CBC = 2;
/**
- * Metadata describing the structure of a (at least partially) encrypted
- * input sample.
- * A buffer's data is considered to be partitioned into "subSamples",
- * each subSample starts with a (potentially empty) run of plain,
- * unencrypted bytes followed by a (also potentially empty) run of
- * encrypted bytes. If pattern encryption applies, each of the latter runs
- * is encrypted only partly, according to a repeating pattern of "encrypt"
- * and "skip" blocks. numBytesOfClearData can be null to indicate that all
- * data is encrypted. This information encapsulates per-sample metadata as
- * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
- * media file format files".
+ * Metadata describing the structure of an encrypted input sample.
+ * <p>
+ * A buffer's data is considered to be partitioned into "subSamples". Each subSample starts with
+ * a run of plain, unencrypted bytes followed by a run of encrypted bytes. Either of these runs
+ * may be empty. If pattern encryption applies, each of the encrypted runs is encrypted only
+ * partly, according to a repeating pattern of "encrypt" and "skip" blocks.
+ * {@link #numBytesOfClearData} can be null to indicate that all data is encrypted, and
+ * {@link #numBytesOfEncryptedData} can be null to indicate that all data is clear. At least one
+ * of {@link #numBytesOfClearData} and {@link #numBytesOfEncryptedData} must be non-null.
+ * <p>
+ * This information encapsulates per-sample metadata as outlined in ISO/IEC FDIS 23001-7:2016
+ * "Common encryption in ISO base media file format files".
+ * <p>
+ * <h3>ISO-CENC Schemes</h3>
+ * ISO/IEC FDIS 23001-7:2016 defines four possible schemes by which media may be encrypted,
+ * corresponding to each possible combination of an AES mode with the presence or absence of
+ * patterned encryption.
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th> </th>
+ * <th>AES-CTR</th>
+ * <th>AES-CBC</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th>Without Patterns</th>
+ * <td>cenc</td>
+ * <td>cbc1</td>
+ * </tr><tr>
+ * <th>With Patterns</th>
+ * <td>cens</td>
+ * <td>cbcs</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * For {@code CryptoInfo}, the scheme is selected implicitly by the combination of the
+ * {@link #mode} field and the value set with {@link #setPattern}. For the pattern, setting the
+ * pattern to all zeroes (that is, both {@code blocksToEncrypt} and {@code blocksToSkip} are
+ * zero) is interpreted as turning patterns off completely. A scheme that does not use patterns
+ * will be selected, either cenc or cbc1. Setting the pattern to any nonzero value will choose
+ * one of the pattern-supporting schemes, cens or cbcs. The default pattern if
+ * {@link #setPattern} is never called is all zeroes.
+ * <p>
+ * <h4>HLS SAMPLE-AES Audio</h4>
+ * HLS SAMPLE-AES audio is encrypted in a manner compatible with the cbcs scheme, except that it
+ * does not use patterned encryption. However, if {@link #setPattern} is used to set the pattern
+ * to all zeroes, this will be interpreted as selecting the cbc1 scheme. The cbc1 scheme cannot
+ * successfully decrypt HLS SAMPLE-AES audio because of differences in how the IVs are handled.
+ * For this reason, it is recommended that a pattern of {@code 1} encrypted block and {@code 0}
+ * skip blocks be used with HLS SAMPLE-AES audio. This will trigger decryption to use cbcs mode
+ * while still decrypting every block.
*/
public final static class CryptoInfo {
/**
@@ -2377,11 +2421,13 @@
*/
public int numSubSamples;
/**
- * The number of leading unencrypted bytes in each subSample.
+ * The number of leading unencrypted bytes in each subSample. If null, all bytes are treated
+ * as encrypted and {@link #numBytesOfEncryptedData} must be specified.
*/
public int[] numBytesOfClearData;
/**
- * The number of trailing encrypted bytes in each subSample.
+ * The number of trailing encrypted bytes in each subSample. If null, all bytes are treated
+ * as clear and {@link #numBytesOfClearData} must be specified.
*/
public int[] numBytesOfEncryptedData;
/**
@@ -2400,35 +2446,34 @@
public int mode;
/**
- * Metadata describing an encryption pattern for the protected bytes in
- * a subsample. An encryption pattern consists of a repeating sequence
- * of crypto blocks comprised of a number of encrypted blocks followed
- * by a number of unencrypted, or skipped, blocks.
+ * Metadata describing an encryption pattern for the protected bytes in a subsample. An
+ * encryption pattern consists of a repeating sequence of crypto blocks comprised of a
+ * number of encrypted blocks followed by a number of unencrypted, or skipped, blocks.
*/
public final static class Pattern {
/**
- * Number of blocks to be encrypted in the pattern. If zero, pattern
- * encryption is inoperative.
+ * Number of blocks to be encrypted in the pattern. If both this and
+ * {@link #mSkipBlocks} are zero, pattern encryption is inoperative.
*/
private int mEncryptBlocks;
/**
- * Number of blocks to be skipped (left clear) in the pattern. If zero,
- * pattern encryption is inoperative.
+ * Number of blocks to be skipped (left clear) in the pattern. If both this and
+ * {@link #mEncryptBlocks} are zero, pattern encryption is inoperative.
*/
private int mSkipBlocks;
/**
- * Construct a sample encryption pattern given the number of blocks to
- * encrypt and skip in the pattern.
+ * Construct a sample encryption pattern given the number of blocks to encrypt and skip
+ * in the pattern. If both parameters are zero, pattern encryption is inoperative.
*/
public Pattern(int blocksToEncrypt, int blocksToSkip) {
set(blocksToEncrypt, blocksToSkip);
}
/**
- * Set the number of blocks to encrypt and skip in a sample encryption
- * pattern.
+ * Set the number of blocks to encrypt and skip in a sample encryption pattern. If both
+ * parameters are zero, pattern encryption is inoperative.
*/
public void set(int blocksToEncrypt, int blocksToSkip) {
mEncryptBlocks = blocksToEncrypt;
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 14dcced..234a4f4 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -40,6 +40,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Allows an app to interact with an active {@link MediaSession2} or a
* {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
* the session.
@@ -735,8 +736,10 @@
return mProvider.getPlaylistMetadata_impl();
}
+
/**
- * Inserts the media item to the playlist at position index.
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
* <p>
* This will not change the currently playing media item.
* If index is less than or equal to the current index of the playlist,
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 8d62bd4..1967a1c 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -28,6 +28,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* A class with information on a single media item with the metadata information.
* Media item are application dependent so we cannot guarantee that they contain the right values.
* <p>
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index f3684d6..034d17e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -34,6 +34,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Base class for media library services.
* <p>
* Media library services enable applications to browse media content provided by an application
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 2ba66b2..1a15962 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -31,6 +31,7 @@
import java.util.Set;
/**
+ * @hide
* Contains metadata about an item, such as the title, artist, etc.
*/
// New version of MediaMetadata with following changes
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index ece19b9..dcc872c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -43,6 +43,7 @@
/**
+ * @hide
* 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}.
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 48b7a51..1fcf02b 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -26,6 +26,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Base class for all media players that want media session.
*/
public abstract class MediaPlayerBase implements AutoCloseable {
@@ -306,8 +307,9 @@
public static abstract class PlayerEventCallback {
/**
* Called when the player's current data source has changed.
+ *
* @param mpb the player whose data source changed.
- * @param dsd the new current data source.
+ * @param dsd the new current data source. null, if no more data sources available.
*/
public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
@Nullable DataSourceDesc dsd) { }
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
index 1250810..453e24a 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -30,6 +30,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
* to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
* notify methods to signal MediaSession2 that playlist-related state has changed.
@@ -228,10 +229,15 @@
}
/**
- * Adds the media item to the playlist at the index
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
+ * <p>
+ * This will not change the currently playing media item.
+ * If index is less than or equal to the current index of the playlist,
+ * the current index of the playlist will be incremented correspondingly.
*
- * @param index index
- * @param item media item to add
+ * @param index the index you want to add
+ * @param item the media item you want to add
*/
public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
mProvider.addPlaylistItem_impl(index, item);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 472d942..0f4b5da 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -49,6 +49,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Allows a media app to expose its transport controls and playback information in a process to
* other processes including the Android framework and other apps. Common use cases are as follows.
* <ul>
@@ -1541,7 +1542,7 @@
* @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
*/
public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
- // TODO(jaewan): Implement (b/74090741).
+ mProvider.setOnDataSourceMissingHelper_impl(helper);
}
/**
@@ -1550,7 +1551,7 @@
* @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
*/
public void clearOnDataSourceMissingHelper() {
- // TODO(jaewan): Implement (b/74090741)
+ mProvider.clearOnDataSourceMissingHelper_impl();
}
/**
@@ -1646,7 +1647,8 @@
}
/**
- * Adds the media item to the playlist at position index.
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
* <p>
* This will not change the currently playing media item.
* If index is less than or equal to the current index of the play list,
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index b830694..85ac9b2 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
/**
+ * @hide
* Base class for media session services, which is the service version of the {@link MediaSession2}.
* <p>
* It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index 68a5641..f088be3 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -28,6 +28,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
* If it's representing a session service, it may not be ongoing.
* <p>
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 8501924..2d96d096 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -26,6 +26,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session. The provider must call
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 963457b..56664a9 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -52,12 +52,13 @@
void setOnMediaKeyListener(in IOnMediaKeyListener listener);
// MediaSession2
- boolean isTrusted(int uid, String packageName);
+ boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
boolean createSession2(in Bundle sessionToken);
void destroySession2(in Bundle sessionToken);
- List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
+ List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
+ String packageName);
void addSessionTokensListener(in ISessionTokensListener listener, int userId,
String packageName);
- void removeSessionTokensListener(in ISessionTokensListener listener);
+ void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 051321c..b7f4998 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -342,16 +342,17 @@
/**
* Returns whether the api
*
- * @param uid uid of the app
* @param packageName packageName
+ * @param pid pid of the app
+ * @param uid uid of the app
* @hide
*/
- public boolean isTrusted(int uid, @NonNull String packageName) {
+ public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
if (packageName == null) {
return false;
}
try {
- return mService.isTrusted(uid, packageName);
+ return mService.isTrusted(packageName, pid, uid);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
}
@@ -390,6 +391,7 @@
}
/**
+ * @hide
* Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
* active sessions regardless of whether they're {@link MediaSession2} or
* {@link MediaSessionService2}.
@@ -403,7 +405,8 @@
public List<SessionToken2> getActiveSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+ /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -412,6 +415,7 @@
}
/**
+ * @hide
* Get {@link List} of {@link SessionToken2} for {@link MediaSessionService2} regardless of their
* activeness. This list represents media apps that support background playback.
* <p>
@@ -424,7 +428,8 @@
public List<SessionToken2> getSessionServiceTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+ /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -433,6 +438,7 @@
}
/**
+ * @hide
* Get all {@link SessionToken2}s. This is the combined list of {@link #getActiveSessionTokens()}
* and {@link #getSessionServiceTokens}.
* <p>
@@ -447,7 +453,8 @@
public List<SessionToken2> getAllSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+ /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -456,6 +463,7 @@
}
/**
+ * @hide
* Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
* <p>
* This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
@@ -508,6 +516,7 @@
}
/**
+ * @hide
* Stop receiving session token updates on the specified listener.
*
* @param listener The listener to remove.
@@ -521,7 +530,7 @@
SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
if (wrapper != null) {
try {
- mService.removeSessionTokensListener(wrapper.mStub);
+ mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error in removeSessionTokensListener.", e);
} finally {
@@ -666,6 +675,7 @@
}
/**
+ * @hide
* Listens for changes to the {@link #getAllSessionTokens()}. This can be added
* using {@link #addOnActiveSessionsChangedListener}.
*/
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index e5ea386..0faed9d 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -21,7 +21,6 @@
import android.media.MediaItem2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.PlayerEventCallback;
import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
@@ -29,6 +28,7 @@
import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
import android.media.MediaSession2.SessionCallback;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
@@ -68,6 +68,8 @@
int getPlayerState_impl();
long getPosition_impl();
long getBufferedPosition_impl();
+ void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
+ void clearOnDataSourceMissingHelper_impl();
interface CommandProvider {
int getCommandCode_impl();
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 98ef62ad7..1a9ce95 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -150,6 +150,13 @@
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
+ <!-- Connected devices settings. Message when Bluetooth is connected and active, showing remote device status and battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_active_battery_level">Active, <xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_active_no_battery_level">Active</string>
+
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the headset or handsfree profile. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 3a0ae9f..093b1bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
@@ -59,6 +60,8 @@
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -135,6 +138,38 @@
final BackgroundHandler mBackgroundHandler;
final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
+ /** Requests that the home app is loaded. */
+ public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
+
+ /** Requests that icons are loaded. */
+ public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
+
+ /** Requests that sizes are loaded. */
+ public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
+
+ /** Requests that launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
+
+ /** Requests that leanback launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
+
+ /**
+ * Flags to configure the session to request various types of info.
+ */
+ @IntDef(prefix = { "FLAG_SESSION_" }, value = {
+ FLAG_SESSION_REQUEST_HOME_APP,
+ FLAG_SESSION_REQUEST_ICONS,
+ FLAG_SESSION_REQUEST_SIZES,
+ FLAG_SESSION_REQUEST_LAUNCHER,
+ FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionFlags {}
+
+ public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+ FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
+ FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+
private ApplicationsState(Application app) {
mContext = app;
mPm = mContext.getPackageManager();
@@ -265,7 +300,8 @@
}
}
- private void clearEntries() {
+ @VisibleForTesting
+ void clearEntries() {
for (int i = 0; i < mEntriesMap.size(); i++) {
mEntriesMap.valueAt(i).clear();
}
@@ -346,7 +382,7 @@
if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
- if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
mBackgroundHandler.post(
() -> {
try {
@@ -595,6 +631,7 @@
}
public class Session implements LifecycleObserver {
+
final Callbacks mCallbacks;
boolean mResumed;
@@ -609,6 +646,7 @@
boolean mRebuildForeground;
private final boolean mHasLifecycle;
+ @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
@@ -620,6 +658,14 @@
}
}
+ public @SessionFlags int getSessionFlags() {
+ return mFlags;
+ }
+
+ public void setSessionFlags(@SessionFlags int flags) {
+ mFlags = flags;
+ }
+
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
@@ -833,10 +879,11 @@
private class BackgroundHandler extends Handler {
static final int MSG_REBUILD_LIST = 1;
static final int MSG_LOAD_ENTRIES = 2;
- static final int MSG_LOAD_ICONS = 3;
- static final int MSG_LOAD_SIZES = 4;
- static final int MSG_LOAD_LAUNCHER = 5;
- static final int MSG_LOAD_HOME_APP = 6;
+ static final int MSG_LOAD_HOME_APP = 3;
+ static final int MSG_LOAD_LAUNCHER = 4;
+ static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
+ static final int MSG_LOAD_ICONS = 6;
+ static final int MSG_LOAD_SIZES = 7;
boolean mRunning;
@@ -860,6 +907,8 @@
}
}
+ int flags = getCombinedSessionFlags(mSessions);
+
switch (msg.what) {
case MSG_REBUILD_LIST: {
} break;
@@ -889,8 +938,8 @@
// happens because of the way we generate the list in
// doResumeIfNeededLocked.
AppEntry entry = mEntriesMap.get(0).get(info.packageName);
- if (entry != null &&
- (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ if (entry != null && !hasFlag(entry.info.flags,
+ ApplicationInfo.FLAG_INSTALLED)) {
mEntriesMap.get(0).remove(info.packageName);
mAppEntries.remove(entry);
}
@@ -909,166 +958,206 @@
}
} break;
case MSG_LOAD_HOME_APP: {
- final List<ResolveInfo> homeActivities = new ArrayList<>();
- mPm.getHomeActivities(homeActivities);
- synchronized (mEntriesMap) {
- final int entryCount = mEntriesMap.size();
- for (int i = 0; i < entryCount; i++) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
- final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- for (ResolveInfo activity : homeActivities) {
- String packageName = activity.activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.isHomeApp = true;
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+ mPm.getHomeActivities(homeActivities);
+ synchronized (mEntriesMap) {
+ final int entryCount = mEntriesMap.size();
+ for (int i = 0; i < entryCount; i++) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+ final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
+ i);
+ for (ResolveInfo activity : homeActivities) {
+ String packageName = activity.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.isHomeApp = true;
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
}
sendEmptyMessage(MSG_LOAD_LAUNCHER);
- }
- break;
- case MSG_LOAD_LAUNCHER: {
- Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER);
- for (int i = 0; i < mEntriesMap.size(); i++) {
- int userId = mEntriesMap.keyAt(i);
- // If we do not specify MATCH_DIRECT_BOOT_AWARE or
- // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
- // according to the user's lock state. When the user is locked, components
- // with ComponentInfo#directBootAware == false will be filtered. We should
- // explicitly include both direct boot aware and unaware components here.
- List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
- launchIntent,
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId
- );
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
- HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- final int N = intents.size();
- for (int j = 0; j < N; j++) {
- String packageName = intents.get(j).activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.hasLauncherEntry = true;
- } else {
- Log.w(TAG, "Cannot find pkg: " + packageName
- + " on user " + userId);
+ } break;
+ case MSG_LOAD_LAUNCHER:
+ case MSG_LOAD_LEANBACK_LAUNCHER: {
+ if ((msg.what == MSG_LOAD_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
+ || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
+
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+ launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
+ ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ int userId = mEntriesMap.keyAt(i);
+ // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+ // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+ // according to the user's lock state. When the user is locked,
+ // components
+ // with ComponentInfo#directBootAware == false will be filtered. We should
+ // explicitly include both direct boot aware and unaware components here.
+ List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+ launchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId
+ );
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+ HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+ final int N = intents.size();
+ for (int j = 0; j < N; j++) {
+ ResolveInfo resolveInfo = intents.get(j);
+ String packageName = resolveInfo.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.hasLauncherEntry = true;
+ entry.launcherEntryEnabled |=
+ resolveInfo.activityInfo.enabled;
+ } else {
+ Log.w(TAG, "Cannot find pkg: " + packageName
+ + " on user " + userId);
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+ }
+
+ if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
}
}
-
- if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+ if (msg.what == MSG_LOAD_LAUNCHER) {
+ sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
+ } else {
+ sendEmptyMessage(MSG_LOAD_ICONS);
}
- sendEmptyMessage(MSG_LOAD_ICONS);
} break;
case MSG_LOAD_ICONS: {
- int numDone = 0;
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
- for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
- AppEntry entry = mAppEntries.get(i);
- if (entry.icon == null || !entry.mounted) {
- synchronized (entry) {
- if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+ for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.icon == null || !entry.mounted) {
+ synchronized (entry) {
+ if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ numDone++;
+ }
+ }
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+ }
+ if (numDone > 0) {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+ }
+ }
+ if (numDone >= 2) {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ break;
+ }
+ }
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ } break;
+ case MSG_LOAD_SIZES: {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+ if (mCurComputingSizePkg != null) {
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: currently computing");
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ for (int i = 0; i < mAppEntries.size(); i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
+ && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
+ if (entry.sizeLoadStart == 0 ||
+ (entry.sizeLoadStart < (now - 20 * 1000))) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
mMainHandler.sendMessage(m);
}
- numDone++;
+ entry.sizeLoadStart = now;
+ mCurComputingSizeUuid = entry.info.storageUuid;
+ mCurComputingSizePkg = entry.info.packageName;
+ mCurComputingSizeUserId = UserHandle.getUserId(
+ entry.info.uid);
+
+ mBackgroundHandler.post(() -> {
+ try {
+ final StorageStats stats =
+ mStats.queryStatsForPackage(
+ mCurComputingSizeUuid,
+ mCurComputingSizePkg,
+ UserHandle.of(
+ mCurComputingSizeUserId));
+ final PackageStats legacy = new PackageStats(
+ mCurComputingSizePkg,
+ mCurComputingSizeUserId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacy,
+ true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ });
}
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: now computing");
+ return;
}
}
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
- }
- if (numDone > 0) {
- if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
- }
- }
- if (numDone >= 2) {
- sendEmptyMessage(MSG_LOAD_ICONS);
- } else {
- sendEmptyMessage(MSG_LOAD_SIZES);
- }
- } break;
- case MSG_LOAD_SIZES: {
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
- if (mCurComputingSizePkg != null) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mAppEntries.size(); i++) {
- AppEntry entry = mAppEntries.get(i);
- if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
- && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
- if (entry.sizeLoadStart == 0 ||
- (entry.sizeLoadStart < (now-20*1000))) {
- if (!mRunning) {
- mRunning = true;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
- mMainHandler.sendMessage(m);
- }
- entry.sizeLoadStart = now;
- mCurComputingSizeUuid = entry.info.storageUuid;
- mCurComputingSizePkg = entry.info.packageName;
- mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-
- mBackgroundHandler.post(() -> {
- try {
- final StorageStats stats = mStats.queryStatsForPackage(
- mCurComputingSizeUuid, mCurComputingSizePkg,
- UserHandle.of(mCurComputingSizeUserId));
- final PackageStats legacy = new PackageStats(
- mCurComputingSizePkg, mCurComputingSizeUserId);
- legacy.codeSize = stats.getCodeBytes();
- legacy.dataSize = stats.getDataBytes();
- legacy.cacheSize = stats.getCacheBytes();
- try {
- mStatsObserver.onGetStatsCompleted(legacy, true);
- } catch (RemoteException ignored) {
- }
- } catch (NameNotFoundException | IOException e) {
- Log.w(TAG, "Failed to query stats: " + e);
- try {
- mStatsObserver.onGetStatsCompleted(null, false);
- } catch (RemoteException ignored) {
- }
- }
-
- });
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
- return;
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+ mRunning = false;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+ mMainHandler.sendMessage(m);
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
- if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
- mRunning = false;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
- mMainHandler.sendMessage(m);
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
} break;
}
}
+ private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+ synchronized (mEntriesMap) {
+ int flags = 0;
+ for (Session session : sessions) {
+ flags |= session.mFlags;
+ }
+ return flags;
+ }
+ }
+
final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
if (!succeeded) {
@@ -1257,6 +1346,11 @@
public boolean hasLauncherEntry;
/**
+ * Whether the component that has a launcher intent filter is enabled.
+ */
+ public boolean launcherEntryEnabled;
+
+ /**
* Whether or not it's a Home app.
*/
public boolean isHomeApp;
@@ -1283,7 +1377,7 @@
// A location where extra info can be placed to be used by custom filters.
public Object extraInfo;
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public AppEntry(Context context, ApplicationInfo info, long id) {
apkFile = new File(info.sourceDir);
this.id = id;
@@ -1336,6 +1430,10 @@
}
}
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
+
/**
* Compare by label, then package name, then uid.
*/
@@ -1449,13 +1547,13 @@
public boolean filterApp(AppEntry entry) {
if (AppUtils.isInstant(entry.info)) {
return false;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
} else if (entry.hasLauncherEntry) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
return true;
}
return false;
@@ -1486,9 +1584,9 @@
@Override
public boolean filterApp(AppEntry entry) {
- if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
}
return false;
@@ -1547,7 +1645,7 @@
@Override
public boolean filterApp(AppEntry entry) {
return !AppUtils.isInstant(entry.info)
- && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
}
};
@@ -1589,7 +1687,7 @@
// TODO: Update for the new game category.
boolean isGame;
synchronized (info.info) {
- isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+ isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
|| info.info.category == ApplicationInfo.CATEGORY_GAME;
}
return isGame;
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 3c02f6a..2d3dfe4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -59,7 +59,8 @@
@Override
public boolean isAvailable() {
- return mContext.getSystemService(UserManager.class).isAdminUser();
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ return um != null && (um.isAdminUser() || um.isDemoUser());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
index 85bf4e8..d21da4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -45,8 +45,7 @@
Build.TYPE.equals("eng") ? 1 : 0) != 0;
final boolean hasRestriction = um.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES);
- final boolean isAdmin = um.isAdminUser();
-
- return isAdmin && !hasRestriction && settingEnabled;
+ final boolean isAdminOrDemo = um.isAdminUser() || um.isDemoUser();
+ return isAdminOrDemo && !hasRestriction && settingEnabled;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
new file mode 100644
index 0000000..2dbabe0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.UserIdInt;
+import android.app.ApplicationPackageManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+ ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
+ ApplicationsStateRoboTest.ShadowPackageManager.class})
+public class ApplicationsStateRoboTest {
+
+ private final static String HOME_PACKAGE_NAME = "com.android.home";
+ private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
+
+ /** Class under test */
+ private ApplicationsState mApplicationsState;
+
+ @Mock
+ private Callbacks mCallbacks;
+ @Captor
+ private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
+ @Mock
+ private StorageStatsManager mStorageStatsManager;
+
+ @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+ public static class ShadowIconDrawableFactory {
+
+ @Implementation
+ public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+ return new ColorDrawable(0);
+ }
+ }
+
+ @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+ public static class ShadowPackageManager extends
+ org.robolectric.shadows.ShadowApplicationPackageManager {
+
+ @Implementation
+ public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ outActivities.add(resolveInfo);
+ return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
+ }
+
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
+ @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+ List<ResolveInfo> resolveInfos = new ArrayList<>();
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ resolveInfo.filter = new IntentFilter();
+ resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
+ resolveInfos.add(resolveInfo);
+ return resolveInfos;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Robolectric does not know about the StorageStatsManager as a system service.
+ // Registering a mock of this service as a replacement.
+ ShadowContextImpl shadowContext = Shadow.extract(
+ RuntimeEnvironment.application.getBaseContext());
+ shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+ StorageStats storageStats = new StorageStats();
+ storageStats.codeBytes = 10;
+ storageStats.dataBytes = 20;
+ storageStats.cacheBytes = 30;
+ when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
+ anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+
+ mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+ mApplicationsState.clearEntries();
+ }
+
+ private ApplicationInfo createApplicationInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.sourceDir = "foo";
+ appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ appInfo.storageUuid = UUID.randomUUID();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+
+ private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+ AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
+ appEntry.label = "label";
+ appEntry.mounted = true;
+ return appEntry;
+ }
+
+ private void addApp(String packageName, int id) {
+ ApplicationInfo appInfo = createApplicationInfo(packageName);
+ AppEntry appEntry = createAppEntry(appInfo, id);
+ mApplicationsState.mAppEntries.add(appEntry);
+ mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry);
+ }
+
+ private void processAllMessages() {
+ Handler mainHandler = mApplicationsState.mMainHandler;
+ Handler bkgHandler = mApplicationsState.mBackgroundHandler;
+ ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
+ ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
+ shadowBkgLooper.idle();
+ shadowMainLooper.idle();
+ }
+
+ private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
+ for (AppEntry appEntry : appEntries) {
+ if (appEntry.id == id) {
+ return appEntry;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testDefaultSessionLoadsAll() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ addApp(LAUNCHABLE_PACKAGE_NAME,2);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(2);
+
+ for (AppEntry appEntry : appEntries) {
+ assertThat(appEntry.size).isGreaterThan(0L);
+ assertThat(appEntry.icon).isNotNull();
+ }
+
+ AppEntry homeEntry = findAppEntry(appEntries, 1);
+ assertThat(homeEntry.isHomeApp).isTrue();
+ assertThat(homeEntry.hasLauncherEntry).isFalse();
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 2);
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsIconsOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNotNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsSizesOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isGreaterThan(0L);
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsHomeOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsLeanbackOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isFalse();
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a15f5fc..ccaf3fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -73,11 +73,22 @@
}
@Test
- public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
+ public void isEnabled_settingsOn_noRestriction_notAdmin_notDemo_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
ShadowUserManager.getShadow().setIsAdminUser(false);
+ ShadowUserManager.getShadow().setIsDemoUser(false);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
+
+ @Test
+ public void isEnabled_settingsOn_noRestriction_notAdmin_isDemo_shouldReturnTrue() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ ShadowUserManager.getShadow().setIsAdminUser(false);
+ ShadowUserManager.getShadow().setIsDemoUser(true);
+
+ assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a3e1bc8..bbd3a53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
package com.android.settingslib.testutils.shadow;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.UserManager;
import org.robolectric.RuntimeEnvironment;
@@ -25,6 +26,9 @@
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
+import java.util.List;
+
@Implements(value = UserManager.class, inheritImplementationMethods = true)
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
@@ -49,6 +53,20 @@
return (UserManager) context.getSystemService(Context.USER_SERVICE);
}
+ @Implementation
+ public int[] getProfileIdsWithDisabled(int userId) {
+ return new int[] { 0 };
+ }
+
+ @Implementation
+ public List<UserInfo> getProfiles() {
+ UserInfo userInfo = new UserInfo();
+ userInfo.id = 0;
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(userInfo);
+ return userInfos;
+ }
+
public static ShadowUserManager getShadow() {
return (ShadowUserManager) Shadow.extract(
RuntimeEnvironment.application.getSystemService(UserManager.class));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3467df5..2047f58 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -578,6 +578,9 @@
Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
GlobalSettingsProto.BLE_SCAN_LOW_LATENCY_INTERVAL_MS);
dumpSetting(s, p,
+ Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+ GlobalSettingsProto.BLE_SCAN_BACKGROUND_MODE);
+ dumpSetting(s, p,
Settings.Global.WIFI_SAVED_STATE,
GlobalSettingsProto.WIFI_SAVED_STATE);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 2da03d2..cc6e3bf 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,6 +50,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
+ android:paddingEnd="1dp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
diff --git a/packages/SystemUI/res/values-sw600dp/bools.xml b/packages/SystemUI/res/values-sw600dp/bools.xml
new file mode 100644
index 0000000..05e38bd
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+ <bool name="qs_show_user_switcher_for_single_user">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
new file mode 100644
index 0000000..499e24e
--- /dev/null
+++ b/packages/SystemUI/res/values/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+ <bool name="qs_show_user_switcher_for_single_user">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5b038b1..906ca4a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -79,9 +79,6 @@
<!-- The color of the material notification background when dark -->
<color name="notification_material_background_dark_color">#ff333333</color>
- <!-- The color of the material notification background when low priority -->
- <color name="notification_material_background_low_priority_color">#fff5f5f5</color>
-
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">#FF616161</color>
@@ -91,9 +88,6 @@
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#28000000</color>
- <!-- The color of the ripples on the low priority notifications -->
- <color name="notification_ripple_color_low_priority">#30000000</color>
-
<!-- The color of the ripples on the tinted notifications -->
<color name="notification_ripple_tinted_color">#30ffffff</color>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index dbf1745..0fa6597 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.PorterDuff.Mode;
@@ -49,7 +50,6 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
@@ -257,17 +257,31 @@
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
-
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
-
-
- mMultiUserSwitch.setVisibility(mExpanded
- && UserManager.get(mContext).isUserSwitcherEnabled()
- ? View.VISIBLE : View.INVISIBLE);
-
+ mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
}
+ private boolean showUserSwitcher(boolean isDemo) {
+ if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
+ return false;
+ }
+ UserManager userManager = UserManager.get(mContext);
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
+ return false;
+ }
+ int switchableUserCount = 0;
+ for (UserInfo user : userManager.getUsers(true)) {
+ if (user.supportsSwitchToByUser()) {
+ ++switchableUserCount;
+ if (switchableUserCount > 1) {
+ return true;
+ }
+ }
+ }
+ return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
+ }
+
private void updateListeners() {
if (mListening) {
mUserInfoController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 8c28af5..8b6b5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -99,7 +99,6 @@
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1);
private final int mTintedRippleColor;
- private final int mLowPriorityRippleColor;
protected final int mNormalRippleColor;
private final AccessibilityManager mAccessibilityManager;
private final DoubleTapHelper mDoubleTapHelper;
@@ -134,7 +133,6 @@
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
private final int mNormalColor;
- private final int mLowPriorityColor;
private boolean mIsBelowSpeedBump;
private FalsingManager mFalsingManager;
@@ -191,12 +189,8 @@
setClipChildren(false);
setClipToPadding(false);
mNormalColor = context.getColor(R.color.notification_material_background_color);
- mLowPriorityColor = context.getColor(
- R.color.notification_material_background_low_priority_color);
mTintedRippleColor = context.getColor(
R.color.notification_ripple_tinted_color);
- mLowPriorityRippleColor = context.getColor(
- R.color.notification_ripple_color_low_priority);
mNormalRippleColor = context.getColor(
R.color.notification_ripple_untinted_color);
mFalsingManager = FalsingManager.getInstance(context);
@@ -997,8 +991,6 @@
}
if (withTint && mBgTint != NO_COLOR) {
return mBgTint;
- } else if (mIsBelowSpeedBump) {
- return mLowPriorityColor;
} else {
return mNormalColor;
}
@@ -1007,8 +999,6 @@
protected int getRippleColor() {
if (mBgTint != 0) {
return mTintedRippleColor;
- } else if (mIsBelowSpeedBump) {
- return mLowPriorityRippleColor;
} else {
return mNormalRippleColor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 9bd8080..6b9567d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -88,6 +88,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
public class ExpandableNotificationRow extends ActivatableNotificationView
implements PluginListener<NotificationMenuRowPlugin> {
@@ -179,6 +180,7 @@
private boolean mExpandAnimationRunning;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
+ private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
private View mHelperButton;
private boolean mChildIsExpanding;
@@ -1115,13 +1117,21 @@
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
boolean wasAboveShelf = isAboveShelf();
+ boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
mHeadsupDisappearRunning = headsUpAnimatingAway;
mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ if (changed && mHeadsUpAnimatingAwayListener != null) {
+ mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
+ }
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
+ public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
+ mHeadsUpAnimatingAwayListener = listener;
+ }
+
/**
* @return if the view was just heads upped and is now animating away. During such a time the
* layout needs to be kept consistent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 48828ab..7a7cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -332,6 +332,7 @@
row.setOnExpandClickListener(mPresenter);
row.setInflationCallback(this);
row.setLongPressListener(getNotificationLongClicker());
+ mListContainer.bindRow(row);
mRemoteInputManager.bindRow(row);
// Get the app name.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 1127075..886d6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -75,7 +75,7 @@
if (shouldApply) {
// lets gray it out
int grey = view.getContext().getColor(
- com.android.internal.R.color.notification_icon_default_color);
+ com.android.internal.R.color.notification_default_color_light);
imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
} else {
// lets reset it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
index 0c19ec0..af9a3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
@@ -188,4 +188,11 @@
default void applyExpandAnimationParams(ExpandAnimationParameters params) {}
default void setExpandingNotification(ExpandableNotificationRow row) {}
+
+ /**
+ * Bind a newly created row.
+ *
+ * @param row The notification to bind.
+ */
+ default void bindRow(ExpandableNotificationRow row) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index aecf5fb..41c7559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -177,7 +177,7 @@
mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
getFullyClosedTranslation());
mShelfState.zTranslation = ambientState.getBaseZHeight();
- if (mAmbientState.isDark()) {
+ if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) {
mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
}
float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 5bb85e2..603902a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -165,7 +165,7 @@
mDensity = context.getResources().getDisplayMetrics().densityDpi;
if (mNotification != null) {
setDecorColor(getContext().getColor(
- com.android.internal.R.color.notification_icon_default_color));
+ com.android.internal.R.color.notification_default_color_light));
}
reloadDimens();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 80854ec..2b7949b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -76,7 +76,6 @@
* notification.
*/
private final Context mPackageContext;
- private boolean mIsLowPriority;
public MediaNotificationProcessor(Context context, Context packageContext) {
this(context, packageContext, new ImageGradientColorizer());
@@ -146,10 +145,7 @@
int foregroundColor = selectForegroundColor(backgroundColor, palette);
builder.setColorPalette(backgroundColor, foregroundColor);
} else {
- int id = mIsLowPriority
- ? R.color.notification_material_background_low_priority_color
- : R.color.notification_material_background_color;
- backgroundColor = mContext.getColor(id);
+ backgroundColor = mContext.getColor(R.color.notification_material_background_color);
}
Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
mContext.getResources().getConfiguration().getLayoutDirection() ==
@@ -307,8 +303,4 @@
private boolean isWhite(float[] hslColor) {
return hslColor[2] >= WHITE_MIN_LIGHTNESS;
}
-
- public void setIsLowPriority(boolean isLowPriority) {
- mIsLowPriority = isLowPriority;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 251e04b..78df77f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -122,9 +122,9 @@
mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
- mColor = resolveColor(mExpandButton);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+ mColor = mNotificationHeader.getOriginalIconColor();
getDozer().setColor(mColor);
}
@@ -132,16 +132,6 @@
mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
}
- private int resolveColor(ImageView icon) {
- if (icon != null && icon.getDrawable() != null) {
- ColorFilter filter = icon.getDrawable().getColorFilter();
- if (filter instanceof PorterDuffColorFilter) {
- return ((PorterDuffColorFilter) filter).getColor();
- }
- }
- return 0;
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index f5110a2d..0143d01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -584,15 +584,9 @@
mSbn.getNotification());
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
- if (mIsLowPriority) {
- int backgroundColor = mContext.getColor(
- R.color.notification_material_background_low_priority_color);
- recoveredBuilder.setBackgroundColorHint(backgroundColor);
- }
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
- processor.setIsLowPriority(mIsLowPriority);
processor.processNotification(notification, recoveredBuilder);
}
return createRemoteViews(mReInflateFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index c576801..9ec5609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -30,6 +30,9 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
@@ -43,11 +46,19 @@
private final HeadsUpStatusBarView mHeadsUpStatusBarView;
private final View mClockView;
private final DarkIconDispatcher mDarkIconDispatcher;
+ private final NotificationPanelView mPanelView;
+ private final Consumer<ExpandableNotificationRow>
+ mSetTrackingHeadsUp = this::setTrackingHeadsUp;
+ private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
+ private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
private float mExpandedHeight;
private boolean mIsExpanded;
private float mExpandFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
+ private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+ -> updatePanelTranslation();
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
@@ -75,18 +86,29 @@
headsUpStatusBarView.setOnDrawingRectChangedListener(
() -> updateIsolatedIconLocation(true /* requireUpdate */));
mStackScroller = stackScroller;
- panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
- panelView.setVerticalTranslationListener(this::updatePanelTranslation);
+ mPanelView = panelView;
+ panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
panelView.setHeadsUpAppearanceController(this);
- mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
- mStackScroller.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
- -> updatePanelTranslation());
+ mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mClockView = clockView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mDarkIconDispatcher.addDarkReceiver(this);
}
+
+ public void destroy() {
+ mHeadsUpManager.removeListener(this);
+ mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+ mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
+ mPanelView.setHeadsUpAppearanceController(null);
+ mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+ mDarkIconDispatcher.removeDarkReceiver(this);
+ }
+
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
mNotificationIconAreaController.setIsolatedIconLocation(
mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 2a1813f..d609ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -60,10 +60,6 @@
private final FalsingManager mFalsingManager;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final Handler mHandler;
- protected KeyguardHostView mKeyguardView;
- protected ViewGroup mRoot;
- private boolean mShowingSoon;
- private int mBouncerPromptReason;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -72,7 +68,14 @@
}
};
private final Runnable mRemoveViewRunnable = this::removeView;
+
private int mStatusBarHeight;
+ private float mExpansion;
+ protected KeyguardHostView mKeyguardView;
+ protected ViewGroup mRoot;
+ private boolean mShowingSoon;
+ private int mBouncerPromptReason;
+ private boolean mIsAnimatingAway;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -252,6 +255,7 @@
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
+ mIsAnimatingAway = false;
if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
if (destroyView) {
@@ -267,6 +271,7 @@
* See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
*/
public void startPreHideAnimation(Runnable runnable) {
+ mIsAnimatingAway = true;
if (mKeyguardView != null) {
mKeyguardView.startDisappearAnimation(runnable);
} else if (runnable != null) {
@@ -290,7 +295,16 @@
}
public boolean isShowing() {
- return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE);
+ return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+ && mExpansion == 0;
+ }
+
+ /**
+ * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
+ * hidden yet, {@code false} otherwise.
+ */
+ public boolean isAnimatingAway() {
+ return mIsAnimatingAway;
}
public void prepare() {
@@ -308,7 +322,8 @@
* @see StatusBarKeyguardViewManager#onPanelExpansionChanged
*/
public void setExpansion(float fraction) {
- if (mKeyguardView != null) {
+ mExpansion = fraction;
+ if (mKeyguardView != null && !mIsAnimatingAway) {
float alpha = MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
mKeyguardView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
mKeyguardView.setTranslationY(fraction * mKeyguardView.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6852df6..64e205d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -247,7 +247,7 @@
private int mStackScrollerMeasuringPass;
private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
= new ArrayList<>();
- private Runnable mVerticalTranslationListener;
+ private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
public NotificationPanelView(Context context, AttributeSet attrs) {
@@ -2360,6 +2360,14 @@
@Override
public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+
+ // When we're unpinning the notification via active edge they remain heads-upped,
+ // we need to make sure that an animation happens in this case, otherwise the notification
+ // will stick to the top without any interaction.
+ if (isFullyCollapsed() && headsUp.isHeadsUp()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
+ headsUp.setHeadsUpIsVisible();
+ }
}
@Override
@@ -2423,8 +2431,9 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
- if (mVerticalTranslationListener != null) {
- mVerticalTranslationListener.run();
+ int size = mVerticalTranslationListener.size();
+ for (int i = 0; i < size; i++) {
+ mVerticalTranslationListener.get(i).run();
}
}
@@ -2736,8 +2745,16 @@
mTrackingHeadsUpListeners.add(listener);
}
- public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener = verticalTranslationListener;
+ public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.remove(listener);
+ }
+
+ public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.add(verticalTranslationListener);
+ }
+
+ public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.remove(verticalTranslationListener);
}
public void setHeadsUpAppearanceController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index b448967..c4d7e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -28,6 +28,7 @@
public static final String TAG = PanelBar.class.getSimpleName();
private static final boolean SPEW = false;
private boolean mBouncerShowing;
+ private boolean mExpanded;
public static final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
@@ -71,10 +72,15 @@
: IMPORTANT_FOR_ACCESSIBILITY_AUTO;
setImportantForAccessibility(important);
+ updateVisibility();
if (mPanel != null) mPanel.setImportantForAccessibility(important);
}
+ private void updateVisibility() {
+ mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ }
+
public boolean panelEnabled() {
return true;
}
@@ -124,7 +130,8 @@
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelView pv = mPanel;
- pv.setVisibility(expanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ mExpanded = expanded;
+ updateVisibility();
// adjust any other panels that may be partially visible
if (expanded) {
if (mState == STATE_CLOSED) {
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 feb7dc3..e6a9b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
@@ -209,6 +210,7 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -604,6 +606,8 @@
private View mNavigationBarView;
protected ActivityLaunchAnimator mActivityLaunchAnimator;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private boolean mVibrateOnOpening;
+ private VibratorHelper mVibratorHelper;
@Override
public void start() {
@@ -641,6 +645,9 @@
updateDisplaySize();
Resources res = mContext.getResources();
+ mVibrateOnOpening = mContext.getResources().getBoolean(
+ R.bool.config_vibrateOnIconAnimation);
+ mVibratorHelper = Dependency.get(VibratorHelper.class);
mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
@@ -809,6 +816,10 @@
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ if (mHeadsUpAppearanceController != null) {
+ // This view is being recreated, let's destroy the old one
+ mHeadsUpAppearanceController.destroy();
+ }
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
@@ -2153,6 +2164,9 @@
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mNotificationPanel.isFullyCollapsed()) {
+ if (mVibrateOnOpening) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
mNotificationPanel.expand(true /* animate */);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
} else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
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 a9c467e..56a7b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -150,8 +150,7 @@
mBouncer.setExpansion(expansion);
if (expansion == 1) {
mBouncer.onFullyHidden();
- updateStates();
- } else if (!mBouncer.isShowing()) {
+ } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
mBouncer.show(true /* resetSecuritySelection */, false /* notifyFalsing */);
} else if (noLongerTracking) {
// Notify that falsing manager should stop its session when user stops touching,
@@ -159,6 +158,9 @@
// data.
mBouncer.onFullyShown();
}
+ if (expansion == 0 || expansion == 1) {
+ updateStates();
+ }
}
mLastTracking = tracking;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index b22ce18..0adb439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -64,6 +64,7 @@
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .setUids(null)
.build();
private static final int NO_NETWORK = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
index a36c966..f98b3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -47,6 +47,11 @@
updateRounding(headsUp, true /* animate */);
}
+ public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
+ boolean isAnimatingAway) {
+ updateRounding(row, false /* animate */);
+ }
+
private void updateRounding(ActivatableNotificationView view, boolean animate) {
float topRoundness = getRoundness(view, true /* top */);
float bottomRoundness = getRoundness(view, false /* top */);
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 a572450..dc94203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -109,6 +109,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -3022,6 +3023,12 @@
}
@Override
+ public void bindRow(ExpandableNotificationRow row) {
+ row.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
+ }
+
+ @Override
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
requestChildrenUpdate();
@@ -4525,6 +4532,13 @@
}
/**
+ * Stop a listener from listening to the expandedHeight.
+ */
+ public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ mExpandedHeightListeners.remove(listener);
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
public interface OnEmptySpaceClickListener {
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 34e444e..231a1866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -40,17 +40,22 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ExpandableNotificationRowTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
private NotificationTestHelper mNotificationTestHelper;
+ boolean mHeadsUpAnimatingAway = false;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext);
mGroup = mNotificationTestHelper.createGroup();
+ mGroup.setHeadsUpAnimatingAwayListener(
+ animatingAway -> mHeadsUpAnimatingAway = animatingAway);
}
@Test
@@ -195,4 +200,12 @@
mGroup.getAppOpsOnClickListener().onClick(view);
verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
}
+
+ @Test
+ public void testHeadsUpAnimatingAwayListener() {
+ mGroup.setHeadsUpAnimatingAway(true);
+ Assert.assertEquals(true, mHeadsUpAnimatingAway);
+ mGroup.setHeadsUpAnimatingAway(false);
+ Assert.assertEquals(false, mHeadsUpAnimatingAway);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index c904ef3..7a61bfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.phone;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -26,6 +29,7 @@
import android.view.View;
import android.widget.TextView;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.TestableDependency;
import com.android.systemui.statusbar.CommandQueue;
@@ -46,6 +50,10 @@
@RunWith(AndroidJUnit4.class)
public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
+ private final NotificationStackScrollLayout mStackScroller =
+ mock(NotificationStackScrollLayout.class);
+ private final NotificationPanelView mPanelView = mock(NotificationPanelView.class);
+ private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private ExpandableNotificationRow mFirst;
private HeadsUpStatusBarView mHeadsUpStatusBarView;
@@ -55,7 +63,7 @@
public void setUp() throws Exception {
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
- mDependency.injectMockDependency(DarkIconDispatcher.class);
+ mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
mock(TextView.class));
mHeadsUpManager = mock(HeadsUpManagerPhone.class);
@@ -63,8 +71,8 @@
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mHeadsUpStatusBarView,
- mock(NotificationStackScrollLayout.class),
- mock(NotificationPanelView.class),
+ mStackScroller,
+ mPanelView,
new View(mContext));
mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
}
@@ -110,4 +118,20 @@
mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
}
+
+ @Test
+ public void testDestroy() {
+ reset(mHeadsUpManager);
+ reset(mDarkIconDispatcher);
+ reset(mPanelView);
+ reset(mStackScroller);
+ mHeadsUpAppearanceController.destroy();
+ verify(mHeadsUpManager).removeListener(any());
+ verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
+ verify(mPanelView).removeVerticalTranslationListener(any());
+ verify(mPanelView).removeTrackingHeadsUpListener(any());
+ verify(mPanelView).setHeadsUpAppearanceController(any());
+ verify(mStackScroller).removeOnExpandedHeightListener(any());
+ verify(mStackScroller).removeOnLayoutChangeListener(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index f3a8417..a37947d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -22,8 +22,6 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.calls;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -238,9 +236,19 @@
}
@Test
- public void testIsShowing() {
+ public void testIsShowing_animated() {
Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
- mBouncer.show(true);
+ mBouncer.show(true /* reset */);
+ Assert.assertTrue("Should be showing", mBouncer.isShowing());
+ }
+
+ @Test
+ public void testIsShowing_forSwipeUp() {
+ mBouncer.setExpansion(1f);
+ mBouncer.show(true /* reset */, false /* animated */);
+ Assert.assertFalse("Should only be showing after collapsing notification panel",
+ mBouncer.isShowing());
+ mBouncer.setExpansion(0f);
Assert.assertTrue("Should be showing", mBouncer.isShowing());
}
@@ -269,6 +277,25 @@
}
@Test
+ public void testIsHiding_preHideOrHide() {
+ Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway());
+ mBouncer.startPreHideAnimation(null /* runnable */);
+ Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway());
+ mBouncer.hide(false /* destroyView */);
+ Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway());
+ }
+
+ @Test
+ public void testIsHiding_skipsTranslation() {
+ mBouncer.show(false /* reset */);
+ reset(mKeyguardHostView);
+ mBouncer.startPreHideAnimation(null /* runnable */);
+ mBouncer.setExpansion(0.5f);
+ verify(mKeyguardHostView, never()).setTranslationY(anyFloat());
+ verify(mKeyguardHostView, never()).setAlpha(anyFloat());
+ }
+
+ @Test
public void testIsSecure() {
Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 7ca9d73..f76de5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -19,11 +19,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -32,6 +36,9 @@
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.os.UserManager;
import android.security.IKeyChainService;
import android.support.test.runner.AndroidJUnit4;
@@ -61,6 +68,7 @@
private final UserManager mUserManager = mock(UserManager.class);
private SecurityControllerImpl mSecurityController;
private CountDownLatch mStateChangedLatch;
+ private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
// implementing SecurityControllerCallback
@Override
@@ -72,7 +80,7 @@
public void setUp() throws Exception {
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
mContext.addMockSystemService(Context.USER_SERVICE, mUserManager);
- mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
+ mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mConnectivityManager);
Intent intent = new Intent(IKeyChainService.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
@@ -176,4 +184,12 @@
//assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS));
//assertFalse(mSecurityController.hasCACertInCurrentUser());
}
+
+ @Test
+ public void testNetworkRequest() {
+ verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
+ (NetworkRequest request) -> request.networkCapabilities.getUids() == null
+ && request.networkCapabilities.getCapabilities().length == 0
+ ), any(NetworkCallback.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
index 1d2c01d..28d1aff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -46,6 +46,7 @@
import org.junit.runner.RunWith;
import java.util.HashSet;
+import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -61,7 +62,11 @@
public void setUp() throws Exception {
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
+ mFirst.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
mSecond = testHelper.createRow();
+ mSecond.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
@@ -153,4 +158,24 @@
Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
}
+
+ @Test
+ public void testRoundingUpdatedWhenAnimatingAwayTrue() {
+ mRoundnessManager.setExpanded(0.0f, 0.0f);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ mFirst.setHeadsUpAnimatingAway(true);
+ Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+
+ @Test
+ public void testRoundingUpdatedWhenAnimatingAwayFalse() {
+ mRoundnessManager.setExpanded(0.0f, 0.0f);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ mFirst.setHeadsUpAnimatingAway(true);
+ mFirst.setHeadsUpAnimatingAway(false);
+ Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 0763fa1..39d0070 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1134,14 +1134,38 @@
NUM_CLIENTS_CHANGED = 2;
}
+ // Soft AP channel bandwidth types
+ enum ChannelBandwidth {
+
+ BANDWIDTH_INVALID = 0;
+
+ BANDWIDTH_20_NOHT = 1;
+
+ BANDWIDTH_20 = 2;
+
+ BANDWIDTH_40 = 3;
+
+ BANDWIDTH_80 = 4;
+
+ BANDWIDTH_80P80 = 5;
+
+ BANDWIDTH_160 = 6;
+ }
+
// Type of event being recorded
optional SoftApEventType event_type = 1;
- // Absolute time when event happened
+ // Time passed since last boot in milliseconds
optional int64 time_stamp_millis = 2;
// Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
optional int32 num_connected_clients = 3;
+
+ // Channel frequency used for Soft AP
+ optional int32 channel_frequency = 4;
+
+ // Channel bandwidth used for Soft AP
+ optional ChannelBandwidth channel_bandwidth = 5;
}
// Wps connection metrics
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index eba5d65..009e723 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -80,6 +80,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.autofill.AutofillManagerService.PackageCompatState;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.FileDescriptor;
@@ -651,7 +652,7 @@
/**
* Compatibility mode metadata per package.
*/
- private static final class PackageCompatState {
+ static final class PackageCompatState {
private final long maxVersionCode;
private final String[] urlBarResourceIds;
@@ -662,8 +663,8 @@
@Override
public String toString() {
- return "PackageCompatState: [maxVersionCode=" + maxVersionCode
- + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds) + "]";
+ return "maxVersionCode=" + maxVersionCode
+ + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds);
}
}
@@ -752,6 +753,25 @@
}
}
}
+
+ private void dump(String prefix, PrintWriter pw) {
+ if (mUserSpecs == null) {
+ pw.println("N/A");
+ return;
+ }
+ pw.println();
+ final String prefix2 = prefix + " ";
+ for (int i = 0; i < mUserSpecs.size(); i++) {
+ final int user = mUserSpecs.keyAt(i);
+ pw.print(prefix); pw.print("User: "); pw.println(user);
+ final ArrayMap<String,PackageCompatState> perUser = mUserSpecs.get(i);
+ for (int j = 0; j < perUser.size(); j++) {
+ final String packageName = perUser.keyAt(j);
+ final PackageCompatState state = perUser.valueAt(j);
+ pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+ }
+ }
+ }
}
final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
@@ -1117,6 +1137,7 @@
boolean oldDebug = sDebug;
final String prefix = " ";
+ final String prefix2 = " ";
try {
synchronized (mLock) {
oldDebug = sDebug;
@@ -1141,8 +1162,8 @@
}
mUi.dump(pw);
pw.print("Autofill Compat State: ");
- pw.println(mAutofillCompatState.mUserSpecs);
- pw.print(prefix); pw.print("from settings: ");
+ mAutofillCompatState.dump(prefix2, pw);
+ pw.print(prefix2); pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
}
if (showHistory) {
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 5c41f3f..7bb532e 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -26,6 +26,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -107,6 +108,13 @@
}
@NonNull
+ public static String paramsToString(@NonNull WindowManager.LayoutParams params) {
+ final StringBuilder builder = new StringBuilder(25);
+ params.dumpDimensions(builder);
+ return builder.toString();
+ }
+
+ @NonNull
static ArrayMap<AutofillId, AutofillValue> getFields(@NonNull Dataset dataset) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
final ArrayList<AutofillValue> values = dataset.getFieldValues();
@@ -128,7 +136,7 @@
return log;
}
- public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) {
+ public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable CharSequence text) {
if (text == null) {
pw.println("null");
} else {
@@ -173,6 +181,7 @@
* @param structure Assist structure
* @param urlBarIds list of ids; only the first id found will be sanitized.
*/
+ @Nullable
public static void sanitizeUrlBar(@NonNull AssistStructure structure,
@NonNull String[] urlBarIds) {
final ViewNode urlBarNode = findViewNode(structure, (node) -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 55c0372..1e1de35 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2077,8 +2077,11 @@
if (saveTriggerId != null) {
writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
}
- saveOnAllViewsInvisible =
- (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+ int flags = saveInfo.getFlags();
+ if (mCompatMode) {
+ flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
+ }
+ saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
// We only need to track views if we want to save once they become invisible.
if (saveOnAllViewsInvisible) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 0dbdc13..03c5850 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -206,29 +206,51 @@
@Override
public String toString() {
- return "ViewState: [id=" + id + ", datasetId=" + mDatasetId
- + ", currentValue=" + mCurrentValue
- + ", autofilledValue=" + mAutofilledValue
- + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
+ final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
+ if (mDatasetId != null) {
+ builder.append("datasetId:" ).append(mDatasetId);
+ }
+ builder.append("state:" ).append(getStateAsString());
+ if (mCurrentValue != null) {
+ builder.append("currentValue:" ).append(mCurrentValue);
+ }
+ if (mAutofilledValue != null) {
+ builder.append("autofilledValue:" ).append(mAutofilledValue);
+ }
+ if (mSanitizedValue != null) {
+ builder.append("sanitizedValue:" ).append(mSanitizedValue);
+ }
+ if (mVirtualBounds != null) {
+ builder.append("virtualBounds:" ).append(mVirtualBounds);
+ }
+ return builder.toString();
}
void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("id:" ); pw.println(this.id);
- pw.print(prefix); pw.print("datasetId:" ); pw.println(this.mDatasetId);
+ pw.print(prefix); pw.print("id:" ); pw.println(id);
+ if (mDatasetId != null) {
+ pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
+ }
pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
- pw.print(prefix); pw.print("response:");
- if (mResponse == null) {
- pw.println("N/A");
- } else {
+ if (mResponse != null) {
+ pw.print(prefix); pw.print("response:");
if (sVerbose) {
pw.println(mResponse);
} else {
- pw.println(mResponse.getRequestId());
+ pw.print("id=");pw.println(mResponse.getRequestId());
}
}
- pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
- pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
- pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
- pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+ if (mCurrentValue != null) {
+ pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
+ }
+ if (mAutofilledValue != null) {
+ pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+ }
+ if (mSanitizedValue != null) {
+ pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
+ }
+ if (mVirtualBounds != null) {
+ pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+ }
}
}
\ No newline at end of file
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 ef4656b..edfc412 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -15,6 +15,7 @@
*/
package com.android.server.autofill.ui;
+import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -37,7 +38,6 @@
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -568,12 +568,24 @@
@Override
public String toString() {
- return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId())
- + ", value=" + (value == null ? "null" : value.length() + "_chars")
- + ", filterable=" + filterable
- + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars")
- + ", view=" + view.getAutofillId()
- + "]";
+ final StringBuilder builder = new StringBuilder("ViewItem:[view=")
+ .append(view.getAutofillId());
+ final String datasetId = dataset == null ? null : dataset.getId();
+ if (datasetId != null) {
+ builder.append(", dataset=").append(datasetId);
+ }
+ if (value != null) {
+ // Cannot print value because it could contain PII
+ builder.append(", value=").append(value.length()).append("_chars");
+ }
+ if (filterable) {
+ builder.append(", filterable");
+ }
+ if (filter != null) {
+ // Filter should not have PII, but it could be a huge regexp
+ builder.append(", filter=").append(filter.pattern().length()).append("_chars");
+ }
+ return builder.append(']').toString();
}
}
@@ -583,8 +595,7 @@
boolean fitsSystemWindows, int layoutDirection) {
if (sVerbose) {
Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
- + ", epicenter="+ transitionEpicenter + ", dir=" + layoutDirection
- + ", params=" + p);
+ + ", params=" + paramsToString(p));
}
UiThread.getHandler().post(() -> mWindow.show(p));
}
@@ -616,7 +627,9 @@
* Shows the window.
*/
public void show(WindowManager.LayoutParams params) {
- if (sVerbose) Slog.v(TAG, "show(): showing=" + mShowing + ", params="+ params);
+ if (sVerbose) {
+ Slog.v(TAG, "show(): showing=" + mShowing + ", params=" + paramsToString(params));
+ }
try {
// Okay here is a bit of voodoo - we want to show the window as system
// controlled one so it covers app windows - adjust the params accordingly.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 30d31b8..088f366 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1386,6 +1386,12 @@
}
}
+ private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
+ if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+ nc.addCapability(NET_CAPABILITY_FOREGROUND);
+ }
+ }
+
@Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
@@ -4411,15 +4417,13 @@
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
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
- // onLost and onAvailable callbacks when networks move in and out of the background.
- // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
- // can't request networks.
- nc.addCapability(NET_CAPABILITY_FOREGROUND);
- }
- ensureValidNetworkSpecifier(networkCapabilities);
+ // 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
+ // onLost and onAvailable callbacks when networks move in and out of the background.
+ // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
+ // can't request networks.
+ restrictBackgroundRequestForCaller(nc);
+ ensureValidNetworkSpecifier(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 457564b..955035b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21798,6 +21798,8 @@
"com.android.frameworks.locationtests",
"com.android.frameworks.coretests.privacy",
"com.android.settings.ui",
+ "com.android.perftests.core",
+ "com.android.perftests.multiuser",
};
public boolean startInstrumentation(ComponentName className,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3a23f18..041764f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1660,7 +1660,7 @@
// Check if volume update should be send to Hearing Aid
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- setHearingAidVolume(newIndex);
+ setHearingAidVolume(newIndex, streamType);
}
// Check if volume update should be sent to Hdmi system audio.
@@ -1909,7 +1909,7 @@
}
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- setHearingAidVolume(index);
+ setHearingAidVolume(index, streamType);
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
@@ -2543,14 +2543,24 @@
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
- final boolean ringerModeMute = mRingerMode == AudioManager.RINGER_MODE_VIBRATE
- || mRingerMode == AudioManager.RINGER_MODE_SILENT;
+ final int ringerMode = mRingerMode; // Read ringer mode as reading primitives is atomic
+ final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+ || ringerMode == AudioManager.RINGER_MODE_SILENT;
+ final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+ && isBluetoothScoOn();
+ // Ask audio policy engine to force use Bluetooth SCO channel if needed
+ final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid();
+ sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_VIBRATE_RINGING,
+ shouldRingSco ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE, eventSource, 0);
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
+ final boolean muteAllowedBySco =
+ !(shouldRingSco && streamType == AudioSystem.STREAM_RING);
final boolean shouldZenMute = shouldZenMuteStream(streamType);
final boolean shouldMute = shouldZenMute || (ringerModeMute
- && isStreamAffectedByRingerMode(streamType));
+ && isStreamAffectedByRingerMode(streamType) && muteAllowedBySco);
if (isMuted == shouldMute) continue;
if (!shouldMute) {
// unmute
@@ -3191,6 +3201,8 @@
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource, 0);
+ // Un-mute ringtone stream volume
+ setRingerModeInt(getRingerModeInternal(), false);
}
/** @see AudioManager#isBluetoothScoOn() */
@@ -4783,7 +4795,7 @@
}
public boolean setIndex(int index, int device, String caller) {
- boolean changed = false;
+ boolean changed;
int oldIndex;
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
@@ -4800,7 +4812,7 @@
// - there is no volume index stored for this device on alias stream.
// If changing volume of current device, also change volume of current
// device on aliased stream
- final boolean currentDevice = (device == getDeviceForStream(mStreamType));
+ final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
final int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState aliasStreamState = mStreamStates[streamType];
@@ -4809,12 +4821,22 @@
(changed || !aliasStreamState.hasIndexForDevice(device))) {
final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
aliasStreamState.setIndex(scaledIndex, device, caller);
- if (currentDevice) {
+ if (isCurrentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller);
}
}
}
+ // Mirror changes in SPEAKER ringtone volume on SCO when
+ if (changed && mStreamType == AudioSystem.STREAM_RING
+ && device == AudioSystem.DEVICE_OUT_SPEAKER) {
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ int otherDevice = mIndexMap.keyAt(i);
+ if ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
+ mIndexMap.put(otherDevice, index);
+ }
+ }
+ }
}
}
if (changed) {
@@ -5665,11 +5687,11 @@
makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
}
- private void setHearingAidVolume(int index) {
+ private void setHearingAidVolume(int index, int streamType) {
synchronized (mHearingAidLock) {
if (mHearingAid != null) {
//hearing aid expect volume value in range -128dB to 0dB
- int gainDB = (int)AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index/10,
+ int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10,
AudioSystem.DEVICE_OUT_HEARING_AID);
if (gainDB < BT_HEARING_AID_GAIN_MIN)
gainDB = BT_HEARING_AID_GAIN_MIN;
@@ -5681,7 +5703,7 @@
// must be called synchronized on mConnectedDevices
private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
- setHearingAidVolume(index);
+ setHearingAidVolume(index, AudioSystem.STREAM_MUSIC);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
@@ -5927,7 +5949,8 @@
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
- AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
+ AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE |
+ AudioSystem.DEVICE_OUT_HEARING_AID;
// must be called before removing the device from mConnectedDevices
// Called synchronized on mConnectedDevices
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index e084ff8..d578e95 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -27,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +41,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -150,7 +154,14 @@
update(mUsers, mApps, true);
}
- private boolean hasPermission(PackageInfo app, String permission) {
+ @VisibleForTesting
+ boolean isPreinstalledSystemApp(PackageInfo app) {
+ int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
+ return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
+ }
+
+ @VisibleForTesting
+ boolean hasPermission(PackageInfo app, String permission) {
if (app.requestedPermissions != null) {
for (String p : app.requestedPermissions) {
if (permission.equals(p)) {
@@ -166,14 +177,40 @@
}
private boolean hasRestrictedNetworkPermission(PackageInfo app) {
- int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
- if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
- return true;
- }
+ if (isPreinstalledSystemApp(app)) return true;
return hasPermission(app, CONNECTIVITY_INTERNAL)
|| hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
+ private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
+ // This function defines what it means to hold the permission to use
+ // background networks.
+ return hasPermission(app, CHANGE_NETWORK_STATE)
+ || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
+ || hasPermission(app, CONNECTIVITY_INTERNAL)
+ || hasPermission(app, NETWORK_STACK)
+ // TODO : remove this check (b/31479477). Not all preinstalled apps should
+ // have access to background networks, they should just request the appropriate
+ // permission for their use case from the list above.
+ || isPreinstalledSystemApp(app);
+ }
+
+ public boolean hasUseBackgroundNetworksPermission(int uid) {
+ final String[] names = mPackageManager.getPackagesForUid(uid);
+ if (null == names || names.length == 0) return false;
+ try {
+ // Only using the first package name. There may be multiple names if multiple
+ // apps share the same UID, but in that case they also share permissions so
+ // querying with any of the names will return the same results.
+ final PackageInfo app = mPackageManager.getPackageInfo(names[0], GET_PERMISSIONS);
+ return hasUseBackgroundNetworksPermission(app);
+ } catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + names[0], e);
+ return false;
+ }
+ }
+
private int[] toIntArray(List<Integer> list) {
int[] array = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
@@ -308,4 +345,8 @@
private static void loge(String s) {
Log.e(TAG, s);
}
+
+ private static void loge(String s, Throwable e) {
+ Log.e(TAG, s, e);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a87a113..b5eb8bf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2033,13 +2033,7 @@
public int[] getPendingRecoverySecretTypes() throws RemoteException {
throw new SecurityException("Not implemented");
}
-
- @Override
- public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
- throws RemoteException {
- mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
- }
-
+
@Override
public byte[] startRecoverySession(@NonNull String sessionId,
@NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index a7d32ed..57fb74d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -61,8 +61,6 @@
private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
- private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
/**
* Encrypts the recovery key using both the lock screen hash and the remote storage's public
@@ -298,8 +296,12 @@
*/
public static byte[] packVaultParams(
PublicKey thmPublicKey, long counterId, int maxAttempts, byte[] vaultHandle) {
- // TODO: Check if vaultHandle has exactly the length of VAULT_HANDLE_LENGTH_BYTES somewhere
- return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES)
+ int vaultParamsLength
+ = 65 // public key
+ + 8 // counterId
+ + 4 // maxAttempts
+ + vaultHandle.length;
+ return ByteBuffer.allocate(vaultParamsLength)
.order(ByteOrder.LITTLE_ENDIAN)
.put(SecureBox.encodePublicKey(thmPublicKey))
.putLong(counterId)
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 e03e86f1..e75722a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -47,6 +47,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.internal.util.Preconditions;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.certificate.SigXml;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
@@ -62,6 +63,7 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException;
@@ -221,6 +223,7 @@
if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ mDatabase.setCounterId(userId, uid, new SecureRandom().nextLong());
}
} catch (CertificateEncodingException e) {
Log.e(TAG, "Failed to encode CertPath", e);
@@ -244,11 +247,11 @@
@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.");
+ if (rootCertificateAlias == null) {
+ Log.e(TAG, "rootCertificateAlias is null");
}
+ Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
+ Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
SigXml sigXml;
try {
@@ -291,11 +294,11 @@
/**
* Gets all data necessary to recover application keys on new device.
*
- * @return recovery data
+ * @return KeyChain Snapshot.
+ * @throws ServiceSpecificException if no snapshot is pending.
* @hide
*/
- public @NonNull
- KeyChainSnapshot getKeyChainSnapshot()
+ public @NonNull KeyChainSnapshot getKeyChainSnapshot()
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
@@ -325,7 +328,7 @@
throw new UnsupportedOperationException();
}
- public void setServerParams(byte[] serverParams) throws RemoteException {
+ public void setServerParams(@NonNull byte[] serverParams) throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
@@ -338,8 +341,9 @@
/**
* Sets the recovery status of key with {@code alias} to {@code status}.
*/
- public void setRecoveryStatus(String alias, int status) throws RemoteException {
+ public void setRecoveryStatus(@NonNull String alias, int status) throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(alias, "alias is null");
mDatabase.setRecoveryStatus(Binder.getCallingUid(), alias, status);
}
@@ -364,6 +368,7 @@
@NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(secretTypes, "secretTypes is null");
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
@@ -495,7 +500,14 @@
@NonNull List<KeyChainProtectionParams> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
-
+ if (rootCertificateAlias == null) {
+ Log.e(TAG, "rootCertificateAlias is null");
+ }
+ Preconditions.checkNotNull(sessionId, "invalid session");
+ Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
+ Preconditions.checkNotNull(vaultParams, "vaultParams is null");
+ Preconditions.checkNotNull(vaultChallenge, "vaultChallenge is null");
+ Preconditions.checkNotNull(secrets, "secrets is null");
CertPath certPath;
try {
certPath = verifierCertPath.getCertPath();
@@ -541,6 +553,9 @@
@NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(sessionId, "invalid session");
+ Preconditions.checkNotNull(encryptedRecoveryKey, "encryptedRecoveryKey is null");
+ Preconditions.checkNotNull(applicationKeys, "encryptedRecoveryKey is null");
int uid = Binder.getCallingUid();
RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
if (sessionEntry == null) {
@@ -667,10 +682,13 @@
* Destroys the session with the given {@code sessionId}.
*/
public void closeSession(@NonNull String sessionId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(sessionId, "invalid session");
mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId);
}
public void removeKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -688,6 +706,7 @@
* @return grant alias, which caller can use to access the key.
*/
public String generateKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -726,8 +745,9 @@
*/
public String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
throws RemoteException {
- if (keyBytes == null ||
- keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
+ Preconditions.checkNotNull(alias, "alias is null");
+ Preconditions.checkNotNull(keyBytes, "keyBytes is null");
+ if (keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
Log.e(TAG, "The given key for import doesn't have the required length "
+ RecoverableKeyGenerator.KEY_SIZE_BITS);
throw new ServiceSpecificException(ERROR_INVALID_KEY_FORMAT,
@@ -770,6 +790,7 @@
* @return grant alias, which caller can use to access the key.
*/
public String getKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
return getAlias(userId, uid, alias);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
deleted file mode 100644
index 52381b8..0000000
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
+++ /dev/null
@@ -1,298 +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 com.android.server.locksettings.recoverablekeystore.storage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class provides helper methods serialize and deserialize {@link KeyChainSnapshot}.
- *
- * <p> It is necessary since {@link android.os.Parcelable} is not designed for persistent storage.
- *
- * <p> For every list, length is stored before the elements.
- *
- */
-public class PersistentKeyChainSnapshot {
- private static final int VERSION = 1;
- private static final int NULL_LIST_LENGTH = -1;
-
- private DataInputStream mInput;
- private DataOutputStream mOut;
- private ByteArrayOutputStream mOutStream;
-
- @VisibleForTesting
- PersistentKeyChainSnapshot() {
- }
-
- @VisibleForTesting
- void initReader(byte[] input) {
- mInput = new DataInputStream(new ByteArrayInputStream(input));
- }
-
- @VisibleForTesting
- void initWriter() {
- mOutStream = new ByteArrayOutputStream();
- mOut = new DataOutputStream(mOutStream);
- }
-
- @VisibleForTesting
- byte[] getOutput() {
- return mOutStream.toByteArray();
- }
-
- /**
- * Converts {@link KeyChainSnapshot} to its binary representation.
- *
- * @param snapshot The snapshot.
- *
- * @throws IOException if serialization failed.
- */
- public static byte[] serialize(@NonNull KeyChainSnapshot snapshot) throws IOException {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeInt(VERSION);
- writer.writeKeyChainSnapshot(snapshot);
- return writer.getOutput();
- }
-
- /**
- * deserializes {@link KeyChainSnapshot}.
- *
- * @input input - byte array produced by {@link serialize} method.
- * @throws IOException if parsing failed.
- */
- public static @NonNull KeyChainSnapshot deserialize(@NonNull byte[] input)
- throws IOException {
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(input);
- try {
- int version = reader.readInt();
- if (version != VERSION) {
- throw new IOException("Unsupported version " + version);
- }
- return reader.readKeyChainSnapshot();
- } catch (IOException e) {
- throw new IOException("Malformed KeyChainSnapshot", e);
- }
- }
-
- /**
- * Must be in sync with {@link KeyChainSnapshot.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyChainSnapshot(KeyChainSnapshot snapshot) throws IOException {
- writeInt(snapshot.getSnapshotVersion());
- writeProtectionParamsList(snapshot.getKeyChainProtectionParams());
- writeBytes(snapshot.getEncryptedRecoveryKeyBlob());
- writeKeysList(snapshot.getWrappedApplicationKeys());
-
- writeInt(snapshot.getMaxAttempts());
- writeLong(snapshot.getCounterId());
- writeBytes(snapshot.getServerParams());
- writeBytes(snapshot.getTrustedHardwarePublicKey());
- }
-
- @VisibleForTesting
- KeyChainSnapshot readKeyChainSnapshot() throws IOException {
- int snapshotVersion = readInt();
- List<KeyChainProtectionParams> protectionParams = readProtectionParamsList();
- byte[] encryptedRecoveryKey = readBytes();
- List<WrappedApplicationKey> keysList = readKeysList();
-
- int maxAttempts = readInt();
- long conterId = readLong();
- byte[] serverParams = readBytes();
- byte[] trustedHardwarePublicKey = readBytes();
-
- return new KeyChainSnapshot.Builder()
- .setSnapshotVersion(snapshotVersion)
- .setKeyChainProtectionParams(protectionParams)
- .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
- .setWrappedApplicationKeys(keysList)
- .setMaxAttempts(maxAttempts)
- .setCounterId(conterId)
- .setServerParams(serverParams)
- .setTrustedHardwarePublicKey(trustedHardwarePublicKey)
- .build();
- }
-
- @VisibleForTesting
- void writeProtectionParamsList(
- @NonNull List<KeyChainProtectionParams> ProtectionParamsList) throws IOException {
- writeInt(ProtectionParamsList.size());
- for (KeyChainProtectionParams protectionParams : ProtectionParamsList) {
- writeProtectionParams(protectionParams);
- }
- }
-
- @VisibleForTesting
- List<KeyChainProtectionParams> readProtectionParamsList() throws IOException {
- int length = readInt();
- List<KeyChainProtectionParams> result = new ArrayList<>(length);
- for (int i = 0; i < length; i++) {
- result.add(readProtectionParams());
- }
- return result;
- }
-
- /**
- * Must be in sync with {@link KeyChainProtectionParams.writeToParcel}
- */
- @VisibleForTesting
- void writeProtectionParams(@NonNull KeyChainProtectionParams protectionParams)
- throws IOException {
- if (!ArrayUtils.isEmpty(protectionParams.getSecret())) {
- // Extra security check.
- throw new RuntimeException("User generated secret should not be stored");
- }
- writeInt(protectionParams.getUserSecretType());
- writeInt(protectionParams.getLockScreenUiFormat());
- writeKeyDerivationParams(protectionParams.getKeyDerivationParams());
- writeBytes(protectionParams.getSecret());
- }
-
- @VisibleForTesting
- KeyChainProtectionParams readProtectionParams() throws IOException {
- int userSecretType = readInt();
- int lockScreenUiFormat = readInt();
- KeyDerivationParams derivationParams = readKeyDerivationParams();
- byte[] secret = readBytes();
- return new KeyChainProtectionParams.Builder()
- .setUserSecretType(userSecretType)
- .setLockScreenUiFormat(lockScreenUiFormat)
- .setKeyDerivationParams(derivationParams)
- .setSecret(secret)
- .build();
- }
-
- /**
- * Must be in sync with {@link KeyDerivationParams.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyDerivationParams(@NonNull KeyDerivationParams Params) throws IOException {
- writeInt(Params.getAlgorithm());
- writeBytes(Params.getSalt());
- }
-
- @VisibleForTesting
- KeyDerivationParams readKeyDerivationParams() throws IOException {
- int algorithm = readInt();
- byte[] salt = readBytes();
- return KeyDerivationParams.createSha256Params(salt);
- }
-
- @VisibleForTesting
- void writeKeysList(@NonNull List<WrappedApplicationKey> applicationKeys) throws IOException {
- writeInt(applicationKeys.size());
- for (WrappedApplicationKey keyEntry : applicationKeys) {
- writeKeyEntry(keyEntry);
- }
- }
-
- @VisibleForTesting
- List<WrappedApplicationKey> readKeysList() throws IOException {
- int length = readInt();
- List<WrappedApplicationKey> result = new ArrayList<>(length);
- for (int i = 0; i < length; i++) {
- result.add(readKeyEntry());
- }
- return result;
- }
-
- /**
- * Must be in sync with {@link WrappedApplicationKey.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyEntry(@NonNull WrappedApplicationKey keyEntry) throws IOException {
- mOut.writeUTF(keyEntry.getAlias());
- writeBytes(keyEntry.getEncryptedKeyMaterial());
- writeBytes(keyEntry.getAccount());
- }
-
- @VisibleForTesting
- WrappedApplicationKey readKeyEntry() throws IOException {
- String alias = mInput.readUTF();
- byte[] keyMaterial = readBytes();
- byte[] account = readBytes();
- return new WrappedApplicationKey.Builder()
- .setAlias(alias)
- .setEncryptedKeyMaterial(keyMaterial)
- .setAccount(account)
- .build();
- }
-
- @VisibleForTesting
- void writeInt(int value) throws IOException {
- mOut.writeInt(value);
- }
-
- @VisibleForTesting
- int readInt() throws IOException {
- return mInput.readInt();
- }
-
- @VisibleForTesting
- void writeLong(long value) throws IOException {
- mOut.writeLong(value);
- }
-
- @VisibleForTesting
- long readLong() throws IOException {
- return mInput.readLong();
- }
-
- @VisibleForTesting
- void writeBytes(@Nullable byte[] value) throws IOException {
- if (value == null) {
- writeInt(NULL_LIST_LENGTH);
- return;
- }
- writeInt(value.length);
- mOut.write(value, 0, value.length);
- }
-
- /**
- * Reads @code{byte[]} from current position. Converts {@code null} to an empty array.
- */
- @VisibleForTesting
- @NonNull byte[] readBytes() throws IOException {
- int length = readInt();
- if (length == NULL_LIST_LENGTH) {
- return new byte[]{};
- }
- byte[] result = new byte[length];
- mInput.read(result, 0, result.length);
- return result;
- }
-}
-
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index bda2ed3..2c3d3ab 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -160,7 +160,6 @@
/**
* Type of secret used to generate recovery key. One of
* {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}.
*/
static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3b5b1bf..7348b84 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -608,7 +608,7 @@
*/
private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
int resolvedUserId) {
- if (isCurrentVolumeController(uid, pid)) return;
+ if (isCurrentVolumeController(pid, uid)) return;
if (getContext()
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
@@ -618,13 +618,13 @@
}
}
- private boolean isCurrentVolumeController(int uid, int pid) {
+ private boolean isCurrentVolumeController(int pid, int uid) {
return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
pid, uid) == PackageManager.PERMISSION_GRANTED;
}
private void enforceSystemUiPermission(String action, int pid, int uid) {
- if (!isCurrentVolumeController(uid, pid)) {
+ if (!isCurrentVolumeController(pid, uid)) {
throw new SecurityException("Only system ui may " + action);
}
}
@@ -1501,53 +1501,21 @@
* Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
* permission or an enabled notification listener)
*
- * @param uid uid of the controller app
- * @param packageName package name of the controller app
+ * @param controllerPackageName package name of the controller app
+ * @param controllerPid pid of the controller app
+ * @param controllerUid uid of the controller app
*/
@Override
- public boolean isTrusted(int uid, String packageName) throws RemoteException {
+ public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
+ throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- int userId = UserHandle.getUserId(uid);
- // Sanity check whether uid and packageName matches
- if (uid != mPackageManager.getPackageUid(packageName, 0, userId)) {
- throw new IllegalArgumentException("uid=" + uid + " and packageName="
- + packageName + " doesn't match");
- }
-
- // Check if it's system server or has MEDIA_CONTENT_CONTROL.
- // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
- // check here.
- if (uid == Process.SYSTEM_UID || mPackageManager.checkPermission(
- android.Manifest.permission.MEDIA_CONTENT_CONTROL, packageName, uid)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted"
- + " MEDIA_CONTENT_CONTROL");
- }
-
- // TODO(jaewan): Add hasEnabledNotificationListener(String pkgName) for
- // optimization (Post-P)
- final List<ComponentName> enabledNotificationListeners =
- mNotificationManager.getEnabledNotificationListeners(userId);
- if (enabledNotificationListeners != null) {
- for (int i = 0; i < enabledNotificationListeners.size(); i++) {
- if (TextUtils.equals(packageName,
- enabledNotificationListeners.get(i).getPackageName())) {
- return true;
- }
- }
- }
+ return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
+ controllerPid, controllerUid);
} finally {
Binder.restoreCallingIdentity(token);
}
- if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled notification"
- + " listener");
- }
- return false;
}
/**
@@ -1614,60 +1582,85 @@
destroySession2Internal(token);
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
+ // TODO(jaewan): Make this API take userId as an argument (b/73597722)
@Override
public List<Bundle> getSessionTokens(boolean activeSessionOnly,
- boolean sessionServiceOnly) throws RemoteException {
+ boolean sessionServiceOnly, String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+
List<Bundle> tokens = new ArrayList<>();
- synchronized (mLock) {
- for (Map.Entry<SessionToken2, MediaController2> record
- : mSessionRecords.entrySet()) {
- boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
- boolean isActive = record.getValue() != null;
- if ((activeSessionOnly && !isActive)
- || (sessionServiceOnly && !isSessionService) ){
- continue;
+ try {
+ verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+ synchronized (mLock) {
+ for (Map.Entry<SessionToken2, MediaController2> record
+ : mSessionRecords.entrySet()) {
+ boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
+ boolean isActive = record.getValue() != null;
+ if ((activeSessionOnly && !isActive)
+ || (sessionServiceOnly && !isSessionService)) {
+ continue;
+ }
+ tokens.add(record.getKey().toBundle());
}
- tokens.add(record.getKey().toBundle());
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return tokens;
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
- // TODO(jaewan): "userId != calling user" needs extra protection (b/73226436)
@Override
public void addSessionTokensListener(ISessionTokensListener listener, int userId,
- String packageName) {
- synchronized (mLock) {
- final SessionTokensListenerRecord record =
- new SessionTokensListenerRecord(listener, userId);
- try {
- listener.asBinder().linkToDeath(record, 0);
- } catch (RemoteException e) {
+ String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
+ synchronized (mLock) {
+ final SessionTokensListenerRecord record =
+ new SessionTokensListenerRecord(listener, resolvedUserId);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ }
+ mSessionTokensListeners.add(record);
}
- mSessionTokensListeners.add(record);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
+ // TODO(jaewan): Make this API take userId as an argument (b/73597722)
@Override
- public void removeSessionTokensListener(ISessionTokensListener listener) {
- synchronized (mLock) {
- IBinder listenerBinder = listener.asBinder();
- for (SessionTokensListenerRecord record : mSessionTokensListeners) {
- if (listenerBinder.equals(record.mListener.asBinder())) {
- try {
- listenerBinder.unlinkToDeath(record, 0);
- } catch (NoSuchElementException e) {
+ public void removeSessionTokensListener(ISessionTokensListener listener,
+ String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+ 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;
}
- mSessionTokensListeners.remove(record);
- break;
}
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
+ // For MediaSession
private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
final int uid) {
String packageName = null;
@@ -1687,6 +1680,66 @@
return resolvedUserId;
}
+ // For MediaSession2
+ private int verifySessionsRequest2(int targetUserId, String callerPackageName,
+ int callerPid, int callerUid) throws RemoteException {
+ // Check that they can make calls on behalf of the user and get the final user id.
+ int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
+ targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
+ callerPackageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ if (!hasMediaControlPermission(
+ resolvedUserId, callerPackageName, callerPid, callerUid)) {
+ throw new SecurityException("Missing permission to control media.");
+ }
+ return resolvedUserId;
+ }
+
+ // For MediaSession2
+ private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
+ int pid, int uid) throws RemoteException {
+ // Allow API calls from the System UI
+ if (isCurrentVolumeController(pid, uid)) {
+ return true;
+ }
+
+ // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+ // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+ // check here.
+ if (uid == Process.SYSTEM_UID || getContext().checkPermission(
+ android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ } else if (DEBUG) {
+ Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+ }
+
+ // You may not access another user's content as an enabled listener.
+ final int userId = UserHandle.getUserId(uid);
+ if (resolvedUserId != userId) {
+ return false;
+ }
+
+ // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
+ // String pkgName) to notification team for optimization
+ final List<ComponentName> enabledNotificationListeners =
+ mNotificationManager.getEnabledNotificationListeners(userId);
+ if (enabledNotificationListeners != null) {
+ for (int i = 0; i < enabledNotificationListeners.size(); i++) {
+ if (TextUtils.equals(packageName,
+ enabledNotificationListeners.get(i).getPackageName())) {
+ return true;
+ }
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+ + "notification listener");
+ }
+ return false;
+ }
+
private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 062a6b8..41ed6f2 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -198,7 +198,14 @@
ParcelFileDescriptor fd = null;
try {
fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
- postSuccess(packageName, fd, callback);
+ if (fd == null || !fd.getFileDescriptor().valid()) {
+ Slog.wtf(TAG,
+ "ParcelFileDescriptor.open returned an invalid descriptor for "
+ + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ } else {
+ postSuccess(packageName, fd, callback);
+ }
} catch (FileNotFoundException e) {
Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
+ snapshotProfile, e);
@@ -264,7 +271,7 @@
mHandler.post(() -> {
try {
callback.onError(errCode);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
}
});
@@ -277,8 +284,17 @@
}
mHandler.post(() -> {
try {
- callback.onSuccess(fd);
- } catch (RemoteException e) {
+ // Double check that the descriptor is still valid.
+ // We've seen production issues (b/76028139) where this can turn invalid (there are
+ // suspicions around the finalizer behaviour).
+ if (fd.getFileDescriptor().valid()) {
+ callback.onSuccess(fd);
+ } else {
+ Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for "
+ + packageName);
+ callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ }
+ } catch (Exception e) {
Slog.w(TAG,
"Failed to call onSuccess after profile snapshot for " + packageName, e);
} finally {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 83fe1c9..ad32ed3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -264,13 +264,9 @@
}
public void grantDefaultPermissions(int userId) {
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- grantAllRuntimePermissions(userId);
- } else {
- grantPermissionsToSysComponentsAndPrivApps(userId);
- grantDefaultSystemHandlerPermissions(userId);
- grantDefaultPermissionExceptions(userId);
- }
+ grantPermissionsToSysComponentsAndPrivApps(userId);
+ grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
}
private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
@@ -1247,6 +1243,13 @@
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
+ // For IoT devices, we check the oem partition for default permissions for each app.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+ dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ }
return ret.isEmpty() ? null : ret.toArray(new File[0]);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4c9b585..4dc68ac 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -72,6 +72,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -2583,7 +2584,14 @@
/** {@inheritDoc} */
@Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
- int type = attrs.type;
+ final int type = attrs.type;
+ final boolean isRoundedCornerOverlay =
+ (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+
+ if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
+ != PERMISSION_GRANTED) {
+ return ADD_PERMISSION_DENIED;
+ }
outAppOp[0] = AppOpsManager.OP_NONE;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a76857e..85436da 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1698,7 +1698,10 @@
stack.getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
}
- if (mService.mAppTransition.getRemoteAnimationController() != null) {
+
+ // Delaying animation start isn't compatible with remote animations at all.
+ if (mService.mAppTransition.getRemoteAnimationController() != null
+ && !mSurfaceAnimator.isAnimationStartDelayed()) {
adapter = mService.mAppTransition.getRemoteAnimationController()
.createAnimationAdapter(this, mTmpPoint, mTmpRect);
} else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2094755..3e47ea6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3807,9 +3807,15 @@
// we use relative layering of the IME targets child windows, and place the
// IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
//
+ // In the case the IME target is animating, the animation Z order may be different
+ // than the WindowContainer Z order, so it's difficult to be sure we have the correct
+ // IME target. In this case we just layer the IME over all transitions by placing it in the
+ // above applications layer.
+ //
// In the case where we have no IME target we assign it where it's base layer would
// place it in the AboveAppWindowContainers.
- if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode()
+ if (imeTarget != null && !(imeTarget.inSplitScreenWindowingMode()
+ || imeTarget.mToken.isAppAnimating())
&& (imeTarget.getSurfaceControl() != null)) {
mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d7f480b..c590067 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -99,6 +99,10 @@
mFinishedCallback = new FinishedCallback(this);
final RemoteAnimationTarget[] animations = createAnimations();
+ if (animations.length == 0) {
+ onAnimationFinished();
+ return;
+ }
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
@@ -132,6 +136,8 @@
mPendingAnimations.get(i).createRemoteAppAnimation();
if (target != null) {
targets.add(target);
+ } else {
+ mPendingAnimations.remove(i);
}
}
return targets.toArray(new RemoteAnimationTarget[targets.size()]);
@@ -225,10 +231,8 @@
RemoteAnimationTarget createRemoteAppAnimation() {
final Task task = mAppWindowToken.getTask();
final WindowState mainWindow = mAppWindowToken.findMainWindow();
- if (task == null) {
- return null;
- }
- if (mainWindow == null) {
+ if (task == null || mainWindow == null || mCapturedFinishCallback == null
+ || mCapturedLeash == null) {
return null;
}
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index e5928b1..ba3d091 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -234,6 +234,10 @@
mService.mAnimationTransferMap.put(mAnimation, this);
}
+ boolean isAnimationStartDelayed() {
+ return mAnimationStartDelayed;
+ }
+
/**
* Cancels the animation, and resets the leash.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a22bb00..56b314f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -48,6 +48,7 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -1214,8 +1215,10 @@
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+ final boolean isRoundedCornerOverlay =
+ (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
- session.mCanAddInternalSystemWindow);
+ session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
if (atoken == null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f727296..14680d9 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -53,6 +54,9 @@
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
+ /** {@code true} if this holds the rounded corner overlay */
+ final boolean mRoundedCornerOverlay;
+
// Set if this token was explicitly added by a client, so should
// persist (not be removed) when all windows are removed.
boolean mPersistOnEmpty;
@@ -105,11 +109,18 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens) {
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ false /* roundedCornersOverlay */);
+ }
+
+ WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
+ DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+ mRoundedCornerOverlay = roundedCornerOverlay;
onDisplayChanged(dc);
}
@@ -259,6 +270,12 @@
dc.reParentWindowToken(this);
mDisplayContent = dc;
+ // The rounded corner overlay should not be rotated. We ensure that by moving it outside
+ // the windowing layer.
+ if (mRoundedCornerOverlay) {
+ mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
+ }
+
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
// to another display before the window behind
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index cf42c0c..dcee151 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -125,6 +125,9 @@
}
};
+// Must match the value from GnssMeasurement.java
+static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss_V1_0> gnssHal = nullptr;
sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
@@ -807,7 +810,8 @@
SET(PseudorangeRateUncertaintyMetersPerSecond,
measurement->pseudorangeRateUncertaintyMps);
SET(AccumulatedDeltaRangeState,
- (static_cast<int32_t>(measurement->accumulatedDeltaRangeState)));
+ (static_cast<int32_t>(measurement->accumulatedDeltaRangeState) &
+ !ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0
SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM);
SET(AccumulatedDeltaRangeUncertaintyMeters,
measurement->accumulatedDeltaRangeUncertaintyM);
@@ -888,9 +892,10 @@
if (measurements_v1_1 != NULL) {
translateGnssMeasurement_V1_0(env, &(measurements_v1_1[i].v1_0), object);
- // Set the V1_1 flag
+ // Set the V1_1 flag, and mark that new field has valid information for Java Layer
SET(AccumulatedDeltaRangeState,
- static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState));
+ (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
+ ADR_STATE_HALF_CYCLE_REPORTED));
} else {
translateGnssMeasurement_V1_0(env, &(measurements_v1_0[i]), object);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
index 9213268..e3e61ac 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
@@ -16,9 +16,8 @@
package com.android.server.devicepolicy;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-
-
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
.OWNER_TRANSFER_METADATA_XML;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.TAG_ADMIN_TYPE;
@@ -32,6 +31,7 @@
import android.os.Environment;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Injector;
import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Metadata;
@@ -57,6 +57,7 @@
@RunWith(AndroidJUnit4.class)
public class TransferOwnershipMetadataManagerTest {
+ private final static String TAG = TransferOwnershipMetadataManagerTest.class.getName();
private final static String SOURCE_COMPONENT =
"com.dummy.admin.package/com.dummy.admin.package.SourceClassName";
private final static String TARGET_COMPONENT =
@@ -106,6 +107,22 @@
@Test
public void testLoad() {
TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ final File transferOwnershipMetadataFile =
+ new File(mMockInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML);
+ Log.d(TAG, "testLoad: file path is " + transferOwnershipMetadataFile.getAbsolutePath());
+ Log.d(TAG, "testLoad: file exists? " + transferOwnershipMetadataFile.exists());
+ Log.d(TAG, "testLoad: file mkdir?" + transferOwnershipMetadataFile.mkdir());
+ try {
+ File canonicalFile = transferOwnershipMetadataFile.getCanonicalFile();
+ File parentFile = canonicalFile.getParentFile();
+ Log.d(TAG, "testLoad: file getCanonicalFile?" + canonicalFile);
+ Log.d(TAG, "testLoad: getCanonicalFile.getParentFile " + parentFile);
+ Log.d(TAG, "testLoad: parent mkdirs? " + parentFile.mkdirs());
+ Log.d(TAG, "testLoad: parent exists? " + parentFile.exists());
+ Log.d(TAG, "testLoad: canonical file.mkdir()? " + canonicalFile.mkdir());
+ } catch (IOException e) {
+ Log.d(TAG, "testLoad: failed to get canonical file");
+ }
paramsManager.saveMetadataFile(TEST_PARAMS);
assertEquals(TEST_PARAMS, paramsManager.loadMetadataFile());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 25747b8..0ea2317 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -62,7 +62,6 @@
import java.io.File;
import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -95,7 +94,6 @@
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
- private KeyPair mKeyPair;
private AndroidKeyStoreSecretKey mWrappingKey;
private PlatformEncryptionKey mEncryptKey;
@@ -108,7 +106,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- mKeyPair = SecureBox.genKeyPair();
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
new int[] {TYPE_LOCKSCREEN});
@@ -249,8 +246,8 @@
TEST_RECOVERY_AGENT_UID,
TEST_APP_KEY_ALIAS,
WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
mKeySyncTask.run();
@@ -265,8 +262,8 @@
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
mKeySyncTask.run();
@@ -275,8 +272,8 @@
@Test
public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setServerParams(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
@@ -301,7 +298,7 @@
lockScreenHash,
keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
- mKeyPair.getPublic(),
+ TestData.CERT_1_PUBLIC_KEY,
counterId,
/*maxAttempts=*/ 10,
TEST_VAULT_HANDLE));
@@ -309,8 +306,8 @@
assertThat(applicationKeys).hasSize(1);
assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
- assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
- .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic()));
+ assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+ .isEqualTo(TestData.CERT_PATH_1);
assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
WrappedApplicationKey keyData = applicationKeys.get(0);
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
@@ -335,15 +332,14 @@
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
assertThat(applicationKeys).hasSize(1);
- assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
- .isEqualTo(SecureBox.encodePublicKey(
- TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
+ assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+ .isEqualTo(TestData.CERT_PATH_1);
}
@Test
public void run_setsCorrectSnapshotVersion() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -361,8 +357,8 @@
@Test
public void run_recreatesMissingSnapshot() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -391,8 +387,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -417,8 +413,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -444,8 +440,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -460,10 +456,10 @@
@Test
public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -482,10 +478,10 @@
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
new int[] {1000});
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -499,10 +495,10 @@
@Test
public void run_notifiesNonregisteredAgent() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -562,7 +558,7 @@
private byte[] decryptThmEncryptedKey(
byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
byte[] locallyEncryptedKey = SecureBox.decrypt(
- mKeyPair.getPrivate(),
+ TestData.CERT_1_PRIVATE_KEY,
/*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
/*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
encryptedKey
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index a251c9d..fae48c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -52,6 +52,8 @@
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
private static final byte[] TEST_VAULT_HANDLE =
new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+ private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
+ private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
private static final String SHA_256_ALGORITHM = "SHA-256";
private static final String APPLICATION_KEY_ALGORITHM = "AES";
private static final byte[] LOCK_SCREEN_HASH_1 =
@@ -63,8 +65,7 @@
private static final byte[] RECOVERY_RESPONSE_HEADER =
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
- private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
+
@Test
public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -345,7 +346,7 @@
}
@Test
- public void packVaultParams_returns94Bytes() throws Exception {
+ public void packVaultParams_returnsCorrectSize() throws Exception {
PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
byte[] packedForm = KeySyncUtils.packVaultParams(
@@ -420,6 +421,24 @@
assertArrayEquals(TEST_VAULT_HANDLE, vaultHandle);
}
+ @Test
+ public void packVaultParams_encodesVaultHandleWithLength8AsLastParam() throws Exception {
+ byte[] vaultHandleWithLenght8 = new byte[] {1, 2, 3, 4, 1, 2, 3, 4};
+ byte[] packedForm = KeySyncUtils.packVaultParams(
+ SecureBox.genKeyPair().getPublic(),
+ /*counterId=*/ 10021L,
+ /*maxAttempts=*/ 10,
+ vaultHandleWithLenght8);
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ assertEquals(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES + 8, packedForm.length);
+ byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
+ byte[] vaultHandle = new byte[8];
+ byteBuffer.get(vaultHandle);
+ assertArrayEquals(vaultHandleWithLenght8, vaultHandle);
+ }
+
private static byte[] randomBytes(int n) {
byte[] bytes = new byte[n];
new Random().nextBytes(bytes);
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 260bb0a..0d6d525b 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
@@ -242,8 +242,8 @@
try {
mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
fail("should have thrown");
- } catch (ServiceSpecificException e) {
- assertThat(e.getMessage()).contains("not contain 256 bits");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
}
}
@@ -300,6 +300,23 @@
}
@Test
+ public void initRecoveryService_regeneratesCounterId() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial + 1));
+ Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+
+ assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue();
+ }
+
+ @Test
public void initRecoveryService_throwsIfInvalidCert() throws Exception {
byte[] modifiedCertXml = TestData.getCertXml();
modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
@@ -393,7 +410,7 @@
ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
TestData.getSigXml());
fail("should have thrown");
- } catch (ServiceSpecificException e) {
+ } catch (NullPointerException e) {
assertThat(e.getMessage()).contains("is null");
}
}
@@ -405,7 +422,7 @@
ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
/*recoveryServiceSigFile=*/ null);
fail("should have thrown");
- } catch (ServiceSpecificException e) {
+ } catch (NullPointerException e) {
assertThat(e.getMessage()).contains("is null");
}
}
@@ -558,6 +575,16 @@
}
@Test
+ public void closeSession_throwsIfNullSession() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("invalid");
+ }
+ }
+
+ @Test
public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
try {
mRecoverableKeyStoreManager.startRecoverySession(
@@ -880,6 +907,16 @@
}
@Test
+ public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
+ @Test
public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -913,6 +950,16 @@
assertThat(statuses).containsEntry(alias, status2); // updated
}
+ @Test
+ public void setRecoveryStatus_throwsIfNullAlias() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
private static byte[] encryptedApplicationKey(
SecretKey recoveryKey, byte[] applicationKey) throws Exception {
return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
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 b5d6ce8..4b059c6 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
@@ -1,11 +1,18 @@
package com.android.server.locksettings.recoverablekeystore;
+import static com.google.common.truth.Truth.assertThat;
+
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
-import java.security.cert.CertPath;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
+import java.security.spec.ECPrivateKeySpec;
public final class TestData {
@@ -213,6 +220,44 @@
+ " </value>\n"
+ "</signature>\n";
+ public static final PublicKey CERT_1_PUBLIC_KEY;
+ public static final PrivateKey CERT_1_PRIVATE_KEY;
+
+ static {
+ try {
+ CERT_1_PUBLIC_KEY =
+ SecureBox.decodePublicKey(
+ new byte[] {
+ (byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18,
+ (byte) 0x98, (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4,
+ (byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda, (byte) 0x1c,
+ (byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a,
+ (byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0,
+ (byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
+ (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
+ (byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79,
+ (byte) 0x20, (byte) 0x1d, (byte) 0x91, (byte) 0x55, (byte) 0xb0,
+ (byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b, (byte) 0x32,
+ (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc,
+ (byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
+ (byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa
+ });
+ CERT_1_PRIVATE_KEY =
+ decodePrivateKey(
+ new byte[] {
+ (byte) 0x70, (byte) 0x01, (byte) 0xc7, (byte) 0x87, (byte) 0x32,
+ (byte) 0x2f, (byte) 0x1c, (byte) 0x9a, (byte) 0x6e, (byte) 0xb1,
+ (byte) 0x91, (byte) 0xca, (byte) 0x4e, (byte) 0xb5, (byte) 0x44,
+ (byte) 0xba, (byte) 0xc8, (byte) 0x68, (byte) 0xc6, (byte) 0x0a,
+ (byte) 0x76, (byte) 0xcb, (byte) 0xd3, (byte) 0x63, (byte) 0x67,
+ (byte) 0x7c, (byte) 0xb0, (byte) 0x11, (byte) 0x82, (byte) 0x65,
+ (byte) 0x77, (byte) 0x01
+ });
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
public static byte[] getCertPath1Bytes() {
try {
return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
@@ -256,4 +301,11 @@
public static byte[] getSigXml() {
return THM_SIG_XML.getBytes(StandardCharsets.UTF_8);
}
+
+ private static PrivateKey decodePrivateKey(byte[] keyBytes) throws Exception {
+ assertThat(keyBytes.length).isEqualTo(32);
+ BigInteger priv = new BigInteger(/*signum=*/ 1, keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
+ }
}
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
deleted file mode 100644
index 180345c..0000000
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
+++ /dev/null
@@ -1,331 +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 com.android.server.locksettings.recoverablekeystore.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class PersistentKeyChainSnapshotTest {
-
- private static final String ALIAS = "some_key";
- private static final String ALIAS2 = "another_key";
- private static final byte[] RECOVERY_KEY_MATERIAL = "recovery_key_data"
- .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[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
- private static final int SNAPSHOT_VERSION = 2;
- private static final int MAX_ATTEMPTS = 10;
- private static final long COUNTER_ID = 123456789L;
- private static final byte[] SERVER_PARAMS = "server_params".getBytes(StandardCharsets.UTF_8);
- private static final byte[] ZERO_BYTES = new byte[0];
- private static final byte[] ONE_BYTE = new byte[]{(byte) 11};
- private static final byte[] TWO_BYTES = new byte[]{(byte) 222,(byte) 222};
-
- @Test
- public void testWriteInt() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeInt(Integer.MIN_VALUE);
- writer.writeInt(Integer.MAX_VALUE);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readInt()).isEqualTo(Integer.MIN_VALUE);
- assertThat(reader.readInt()).isEqualTo(Integer.MAX_VALUE);
-
- assertThrows(
- IOException.class,
- () -> reader.readInt());
- }
-
- @Test
- public void testWriteLong() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeLong(Long.MIN_VALUE);
- writer.writeLong(Long.MAX_VALUE);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readLong()).isEqualTo(Long.MIN_VALUE);
- assertThat(reader.readLong()).isEqualTo(Long.MAX_VALUE);
-
- assertThrows(
- IOException.class,
- () -> reader.readLong());
- }
-
- @Test
- public void testWriteBytes() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeBytes(ZERO_BYTES);
- writer.writeBytes(ONE_BYTE);
- writer.writeBytes(TWO_BYTES);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readBytes()).isEqualTo(ZERO_BYTES);
- assertThat(reader.readBytes()).isEqualTo(ONE_BYTE);
- assertThat(reader.readBytes()).isEqualTo(TWO_BYTES);
-
- assertThrows(
- IOException.class,
- () -> reader.readBytes());
- }
-
- @Test
- public void testReadBytes_returnsNullArrayAsEmpty() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeBytes(null);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readBytes()).isEqualTo(new byte[]{}); // null -> empty array
- }
-
- @Test
- public void testWriteKeyEntry() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build();
- writer.writeKeyEntry(entry);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- WrappedApplicationKey copy = reader.readKeyEntry();
- assertThat(copy.getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyEntry());
- }
-
- @Test
- public void testWriteProtectionParams() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
- KeyChainProtectionParams protectionParams = new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build();
- writer.writeProtectionParams(protectionParams);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainProtectionParams copy = reader.readProtectionParams();
- assertThat(copy.getUserSecretType()).isEqualTo(1);
- assertThat(copy.getLockScreenUiFormat()).isEqualTo(2);
- assertThat(copy.getKeyDerivationParams().getSalt()).isEqualTo(SALT);
-
- assertThrows(
- IOException.class,
- () -> reader.readProtectionParams());
- }
-
- @Test
- public void testKeyChainSnapshot() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
-
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
- ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build());
-
- ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
-
- KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
- .setSnapshotVersion(SNAPSHOT_VERSION)
- .setKeyChainProtectionParams(protectionParamsList)
- .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
- .setWrappedApplicationKeys(appKeysList)
- .setMaxAttempts(MAX_ATTEMPTS)
- .setCounterId(COUNTER_ID)
- .setServerParams(SERVER_PARAMS)
- .setTrustedHardwarePublicKey(PUBLIC_KEY)
- .build();
-
- writer.writeKeyChainSnapshot(snapshot);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainSnapshot copy = reader.readKeyChainSnapshot();
- assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
- assertThat(copy.getKeyChainProtectionParams()).hasSize(1);
- assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
- assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
- assertThat(copy.getWrappedApplicationKeys()).hasSize(1);
- assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
- assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
- assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
- assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyChainSnapshot());
-
- verifyDeserialize(snapshot);
- }
-
- @Test
- public void testKeyChainSnapshot_withManyKeysAndProtectionParams() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
-
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
- ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build());
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(2)
- .setLockScreenUiFormat(3)
- .setKeyDerivationParams(derivationParams)
- .build());
- ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS2)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
-
-
- KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
- .setSnapshotVersion(SNAPSHOT_VERSION)
- .setKeyChainProtectionParams(protectionParamsList)
- .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
- .setWrappedApplicationKeys(appKeysList)
- .setMaxAttempts(MAX_ATTEMPTS)
- .setCounterId(COUNTER_ID)
- .setServerParams(SERVER_PARAMS)
- .setTrustedHardwarePublicKey(PUBLIC_KEY)
- .build();
-
- writer.writeKeyChainSnapshot(snapshot);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainSnapshot copy = reader.readKeyChainSnapshot();
- assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
- assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
- assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
- assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
- assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
- assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
- assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyChainSnapshot());
-
- verifyDeserialize(snapshot);
- }
-
- private void verifyDeserialize(KeyChainSnapshot snapshot) throws Exception {
- byte[] serialized = PersistentKeyChainSnapshot.serialize(snapshot);
- KeyChainSnapshot copy = PersistentKeyChainSnapshot.deserialize(serialized);
- assertThat(copy.getSnapshotVersion())
- .isEqualTo(snapshot.getSnapshotVersion());
- assertThat(copy.getKeyChainProtectionParams().size())
- .isEqualTo(copy.getKeyChainProtectionParams().size());
- assertThat(copy.getEncryptedRecoveryKeyBlob())
- .isEqualTo(snapshot.getEncryptedRecoveryKeyBlob());
- assertThat(copy.getWrappedApplicationKeys().size())
- .isEqualTo(snapshot.getWrappedApplicationKeys().size());
- assertThat(copy.getMaxAttempts()).isEqualTo(snapshot.getMaxAttempts());
- assertThat(copy.getCounterId()).isEqualTo(snapshot.getCounterId());
- assertThat(copy.getServerParams()).isEqualTo(snapshot.getServerParams());
- assertThat(copy.getTrustedHardwarePublicKey())
- .isEqualTo(snapshot.getTrustedHardwarePublicKey());
- }
-
- @Test
- public void testDeserialize_failsForNewerVersion() throws Exception {
- byte[] newVersion = new byte[]{(byte) 2, (byte) 0, (byte) 0, (byte) 0};
- assertThrows(
- IOException.class,
- () -> PersistentKeyChainSnapshot.deserialize(newVersion));
- }
-
- @Test
- public void testDeserialize_failsForEmptyData() throws Exception {
- byte[] empty = new byte[]{};
- assertThrows(
- IOException.class,
- () -> PersistentKeyChainSnapshot.deserialize(empty));
- }
-
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index 999dce5..6f2237f 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -75,11 +75,11 @@
Map<String, Boolean> result = PrivacyUtils.createDpEncodedReportMap(false, null,
TEST_DIGEST_LIST, TEST_AGGREGATED_RESULT1);
assertEquals(6, result.size());
- assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
+ assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
- assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
- assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+ assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+ assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 26a7313..64501e4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -68,6 +68,7 @@
super.setUp();
MockitoAnnotations.initMocks(this);
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ mAdapter.setCallingPid(123);
sWm.mH.runWithScissors(() -> {
mHandler = new TestHandler(null, mClock);
}, 0);
@@ -83,7 +84,7 @@
new Point(50, 100), new Rect(50, 100, 150, 150));
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
-
+ sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -167,4 +168,33 @@
mController.goodToGo();
verifyZeroInteractions(mMockRunner);
}
+
+ @Test
+ public void testNotReallyStarted() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ mController.goodToGo();
+ verifyZeroInteractions(mMockRunner);
+ }
+
+ @Test
+ public void testOneNotStarted() throws Exception {
+ final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
+ final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+ mController.createAnimationAdapter(win1.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
+ }
}
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 6506872..16b8458 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -134,6 +134,7 @@
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verifyZeroInteractions(mSpec);
assertAnimating(mAnimatable);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
verify(mSpec).startAnimation(any(), any(), any());
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
new file mode 100644
index 0000000..4a83d1b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.connectivity;
+
+import static android.Manifest.permission.CHANGE_NETWORK_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+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;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PermissionMonitorTest {
+ private static final int MOCK_UID = 10001;
+ private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+
+ @Mock private Context mContext;
+ @Mock private PackageManager mPackageManager;
+
+ private PermissionMonitor mPermissionMonitor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
+ mPermissionMonitor = new PermissionMonitor(mContext, null);
+ }
+
+ private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
+ final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
+ when(mPackageManager.getPackageInfo(MOCK_PACKAGE_NAMES[0], GET_PERMISSIONS))
+ .thenReturn(packageInfo);
+ }
+
+ private PackageInfo packageInfoWithPermissions(String[] permissions, boolean preinstalled) {
+ final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.requestedPermissions = permissions;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags = preinstalled ? FLAG_SYSTEM : 0;
+ return packageInfo;
+ }
+
+ @Test
+ public void testHasPermission() {
+ PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = packageInfoWithPermissions(new String[] {
+ CHANGE_NETWORK_STATE, NETWORK_STACK
+ }, false);
+ assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = packageInfoWithPermissions(new String[] {
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
+ }, false);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+ }
+
+ @Test
+ public void testIsPreinstalledSystemApp() {
+ PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+ assertFalse(mPermissionMonitor.isPreinstalledSystemApp(app));
+
+ app = packageInfoWithPermissions(new String[] {}, true);
+ assertTrue(mPermissionMonitor.isPreinstalledSystemApp(app));
+ }
+
+ @Test
+ public void testHasUseBackgroundNetworksPermission() throws Exception {
+ expectPermission(new String[] { CHANGE_NETWORK_STATE }, false);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ // TODO : make this false when b/31479477 is fixed
+ expectPermission(new String[] {}, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+ expectPermission(new String[] { CHANGE_WIFI_STATE }, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] {}, false);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+ }
+}
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
index 3b0221c..79469e3 100644
--- a/tests/testables/tests/Android.mk
+++ b/tests/testables/tests/Android.mk
@@ -41,5 +41,7 @@
LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_PACKAGE)