diff options
430 files changed, 11794 insertions, 3615 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..45884c46ffea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea +*.iml diff --git a/Android.bp b/Android.bp index b3a2ef895d0a..c568d3676916 100644 --- a/Android.bp +++ b/Android.bp @@ -162,6 +162,7 @@ java_defaults { "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl", "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl", "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl", + "core/java/android/hardware/display/IColorDisplayManager.aidl", "core/java/android/hardware/display/IDisplayManager.aidl", "core/java/android/hardware/display/IDisplayManagerCallback.aidl", "core/java/android/hardware/display/IVirtualDisplayCallback.aidl", diff --git a/Android.mk b/Android.mk index b7dda9a45ed8..92e33e988249 100644 --- a/Android.mk +++ b/Android.mk @@ -76,10 +76,9 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - frameworks/base/config/hiddenapi-light-greylist.txt \ - frameworks/base/config/hiddenapi-vendor-list.txt \ + frameworks/base/config/hiddenapi-greylist.txt \ + frameworks/base/config/hiddenapi-greylist-max-p.txt \ frameworks/base/config/hiddenapi-greylist-max-o.txt \ - frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \ frameworks/base/config/hiddenapi-force-blacklist.txt \ $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ @@ -88,10 +87,9 @@ $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ --csv $(PRIVATE_FLAGS_INPUTS) \ - --greylist \ - frameworks/base/config/hiddenapi-light-greylist.txt \ - frameworks/base/config/hiddenapi-vendor-list.txt \ + --greylist frameworks/base/config/hiddenapi-greylist.txt \ --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ + --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \ --greylist-max-o-ignore-conflicts \ frameworks/base/config/hiddenapi-greylist-max-o.txt \ --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \ diff --git a/api/current.txt b/api/current.txt index 1786a5ef22ac..802e50e5abaf 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7692,6 +7692,7 @@ package android.appwidget { method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); method public void setExecutor(java.util.concurrent.Executor); + method public void setOnLightBackground(boolean); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); @@ -16498,6 +16499,7 @@ package android.hardware.camera2 { method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract java.lang.String getId(); + method public boolean isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; field public static final int TEMPLATE_MANUAL = 6; // 0x6 field public static final int TEMPLATE_PREVIEW = 1; // 0x1 field public static final int TEMPLATE_RECORD = 3; // 0x3 @@ -17095,8 +17097,9 @@ package android.hardware.camera2.params { field public static final int RED = 0; // 0x0 } - public final class SessionConfiguration { + public final class SessionConfiguration implements android.os.Parcelable { ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback); + method public int describeContents(); 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(); @@ -17105,6 +17108,8 @@ package android.hardware.camera2.params { method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback(); method public void setInputConfiguration(android.hardware.camera2.params.InputConfiguration); method public void setSessionParameters(android.hardware.camera2.CaptureRequest); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.SessionConfiguration> CREATOR; field public static final int SESSION_HIGH_SPEED = 1; // 0x1 field public static final int SESSION_REGULAR = 0; // 0x0 } @@ -23436,6 +23441,17 @@ package android.media { method public void onTearDown(android.media.AudioTrack); } + public class CallbackDataSourceDesc extends android.media.DataSourceDesc { + method public android.media.DataSourceCallback getDataSourceCallback(); + } + + public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { + ctor public CallbackDataSourceDesc.Builder(); + ctor public CallbackDataSourceDesc.Builder(android.media.CallbackDataSourceDesc); + method public android.media.CallbackDataSourceDesc build(); + method public android.media.CallbackDataSourceDesc.Builder setDataSource(android.media.DataSourceCallback); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -23488,6 +23504,26 @@ package android.media { field public static final int QUALITY_MEDIUM = 1; // 0x1 } + public abstract class DataSourceCallback implements java.io.Closeable { + ctor public DataSourceCallback(); + method public abstract long getSize() throws java.io.IOException; + method public abstract int readAt(long, byte[], int, int) throws java.io.IOException; + } + + public class DataSourceDesc { + method public long getEndPosition(); + method public java.lang.String getMediaId(); + method public long getStartPosition(); + field public static final long LONG_MAX_TIME_MS = 576460752303423L; // 0x20c49ba5e353fL + field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL + } + + protected static class DataSourceDesc.BuilderBase<T extends android.media.DataSourceDesc.BuilderBase> { + method public T setEndPosition(long); + method public T setMediaId(java.lang.String); + method public T setStartPosition(long); + } + public final class DeniedByServerException extends android.media.MediaDrmException { ctor public DeniedByServerException(java.lang.String); } @@ -23688,6 +23724,21 @@ package android.media { field public static final int EULER_Z = 2; // 0x2 } + public class FileDataSourceDesc extends android.media.DataSourceDesc { + method public long getLength(); + method public long getOffset(); + method public android.os.ParcelFileDescriptor getParcelFileDescriptor(); + field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL + } + + public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { + ctor public FileDataSourceDesc.Builder(); + ctor public FileDataSourceDesc.Builder(android.media.FileDataSourceDesc); + method public android.media.FileDataSourceDesc build(); + method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor); + method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor, long, long); + } + public abstract class Image implements java.lang.AutoCloseable { method public abstract void close(); method public android.graphics.Rect getCropRect(); @@ -25075,6 +25126,166 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } + public class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable { + ctor public MediaPlayer2(android.content.Context); + method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); + method public java.lang.Object attachAuxEffect(int); + method public boolean cancelCommand(java.lang.Object); + method public java.lang.Object clearNextDataSources(); + method public void clearPendingCommands(); + method public void close(); + method public java.lang.Object deselectTrack(int); + method public android.media.AudioAttributes getAudioAttributes(); + method public int getAudioSessionId(); + method public long getBufferedPosition(); + method public android.media.DataSourceDesc getCurrentDataSource(); + method public long getCurrentPosition(); + method public long getDuration(); + method public float getMaxPlayerVolume(); + method public android.os.PersistableBundle getMetrics(); + method public android.media.PlaybackParams getPlaybackParams(); + method public float getPlayerVolume(); + method public android.media.AudioDeviceInfo getPreferredDevice(); + method public android.media.AudioDeviceInfo getRoutedDevice(); + method public int getSelectedTrack(int); + method public int getState(); + method public android.media.SyncParams getSyncParams(); + method public android.media.MediaTimestamp getTimestamp(); + method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(); + method public android.media.VideoSize getVideoSize(); + method public boolean isLooping(); + method public java.lang.Object loopCurrent(boolean); + method public java.lang.Object notifyWhenCommandLabelReached(java.lang.Object); + method public java.lang.Object pause(); + method public java.lang.Object play(); + method public java.lang.Object prepare(); + method public void registerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.EventCallback); + method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); + method public void reset(); + method public java.lang.Object seekTo(long); + method public java.lang.Object seekTo(long, int); + method public java.lang.Object selectTrack(int); + method public java.lang.Object setAudioAttributes(android.media.AudioAttributes); + method public java.lang.Object setAudioSessionId(int); + method public java.lang.Object setAuxEffectSendLevel(float); + method public java.lang.Object setDataSource(android.media.DataSourceDesc); + method public java.lang.Object setDisplay(android.view.SurfaceHolder); + method public java.lang.Object setNextDataSource(android.media.DataSourceDesc); + method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>); + method public java.lang.Object setPlaybackParams(android.media.PlaybackParams); + method public java.lang.Object setPlayerVolume(float); + method public boolean setPreferredDevice(android.media.AudioDeviceInfo); + method public java.lang.Object setScreenOnWhilePlaying(boolean); + method public java.lang.Object setSurface(android.view.Surface); + method public java.lang.Object setSyncParams(android.media.SyncParams); + method public java.lang.Object setWakeLock(android.os.PowerManager.WakeLock); + method public java.lang.Object skipToNext(); + method public void unregisterEventCallback(android.media.MediaPlayer2.EventCallback); + field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1 + field public static final int CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES = 30; // 0x1e + 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_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_DISPLAY = 33; // 0x21 + 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_PLAYER_VOLUME = 26; // 0x1a + field public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35; // 0x23 + 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_SET_WAKE_LOCK = 34; // 0x22 + 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 = 6; // 0x6 + 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 CALL_STATUS_SKIPPED = 5; // 0x5 + 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_DATA_SOURCE_END = 5; // 0x5 + field public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6; // 0x6 + field public static final int MEDIA_INFO_DATA_SOURCE_REPEAT = 7; // 0x7 + field public static final int MEDIA_INFO_DATA_SOURCE_START = 2; // 0x2 + 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_PREPARED = 100; // 0x64 + 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 PLAYER_STATE_ERROR = 1005; // 0x3ed + field public static final int PLAYER_STATE_IDLE = 1001; // 0x3e9 + field public static final int PLAYER_STATE_PAUSED = 1003; // 0x3eb + field public static final int PLAYER_STATE_PLAYING = 1004; // 0x3ec + field public static final int PLAYER_STATE_PREPARED = 1002; // 0x3ea + 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 + } + + public static class MediaPlayer2.EventCallback { + ctor public MediaPlayer2.EventCallback(); + 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 onMediaTimeDiscontinuity(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp); + method public void onSubtitleData(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.SubtitleData); + method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData); + method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.VideoSize); + } + + 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 class MediaPlayer2.TrackInfo { + method public android.media.MediaFormat getFormat(); + method public java.lang.String getLanguage(); + method public int getTrackType(); + 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 class MediaRecorder implements android.media.AudioRouting { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); @@ -25817,6 +26028,26 @@ package android.media { ctor public UnsupportedSchemeException(java.lang.String); } + public class UriDataSourceDesc extends android.media.DataSourceDesc { + method public android.content.Context getContext(); + method public java.util.List<java.net.HttpCookie> getCookies(); + method public java.util.Map<java.lang.String, java.lang.String> getHeaders(); + method public android.net.Uri getUri(); + } + + public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { + ctor public UriDataSourceDesc.Builder(); + ctor public UriDataSourceDesc.Builder(android.media.UriDataSourceDesc); + method public android.media.UriDataSourceDesc build(); + method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri); + method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>); + } + + public final class VideoSize { + method public int getHeight(); + method public int getWidth(); + } + public abstract interface VolumeAutomation { method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration); } @@ -29138,10 +29369,11 @@ package android.net.wifi { field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; - field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2 - field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2 field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1 - field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5 field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0 field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; @@ -34069,6 +34301,7 @@ package android.os { } public final class PowerManager { + method public int getCurrentThermalStatus(); method public int getLocationPowerSaveMode(); method public boolean isDeviceIdleMode(); method public boolean isIgnoringBatteryOptimizations(java.lang.String); @@ -34079,6 +34312,8 @@ package android.os { method public boolean isWakeLockLevelSupported(int); method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String); method public void reboot(java.lang.String); + method public void registerThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback, java.util.concurrent.Executor); + method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback); field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED"; @@ -34093,6 +34328,18 @@ package android.os { field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1 field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6 + field public static final int THERMAL_STATUS_CRITICAL = 4; // 0x4 + field public static final int THERMAL_STATUS_EMERGENCY = 5; // 0x5 + field public static final int THERMAL_STATUS_LIGHT = 1; // 0x1 + field public static final int THERMAL_STATUS_MODERATE = 2; // 0x2 + field public static final int THERMAL_STATUS_NONE = 0; // 0x0 + field public static final int THERMAL_STATUS_SEVERE = 3; // 0x3 + field public static final int THERMAL_STATUS_SHUTDOWN = 6; // 0x6 + } + + public static abstract class PowerManager.ThermalStatusCallback { + ctor public PowerManager.ThermalStatusCallback(); + method public void onStatusChange(int); } public final class PowerManager.WakeLock { @@ -37536,8 +37783,8 @@ package android.provider { field public static final java.lang.String DATE_TAKEN = "datetaken"; field public static final java.lang.String DESCRIPTION = "description"; field public static final java.lang.String IS_PRIVATE = "isprivate"; - field public static final java.lang.String LATITUDE = "latitude"; - field public static final java.lang.String LONGITUDE = "longitude"; + field public static final deprecated java.lang.String LATITUDE = "latitude"; + field public static final deprecated java.lang.String LONGITUDE = "longitude"; field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic"; field public static final java.lang.String ORIENTATION = "orientation"; field public static final deprecated java.lang.String PICASA_ID = "picasa_id"; @@ -37661,8 +37908,8 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String IS_PRIVATE = "isprivate"; field public static final java.lang.String LANGUAGE = "language"; - field public static final java.lang.String LATITUDE = "latitude"; - field public static final java.lang.String LONGITUDE = "longitude"; + field public static final deprecated java.lang.String LATITUDE = "latitude"; + field public static final deprecated java.lang.String LONGITUDE = "longitude"; field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic"; field public static final java.lang.String RESOLUTION = "resolution"; field public static final java.lang.String TAGS = "tags"; @@ -45886,8 +46133,8 @@ package android.text.style { method public deprecated java.lang.String getLocale(); method public java.util.Locale getLocaleObject(); method public int getSpanTypeId(); - method public int getUnderlineColor(); method public java.lang.String[] getSuggestions(); + method public int getUnderlineColor(); method public void setFlags(int); method public void updateDrawState(android.text.TextPaint); method public void writeToParcel(android.os.Parcel, int); @@ -47888,6 +48135,7 @@ package android.view { method public final boolean isFunctionPressed(); method public static final boolean isGamepadButton(int); method public final boolean isLongPress(); + method public static final boolean isMediaSessionKey(int); method public final boolean isMetaPressed(); method public static boolean isModifierKey(int); method public final boolean isNumLockOn(); @@ -55080,6 +55328,7 @@ package android.widget { method public void setInt(int, java.lang.String, int); method public void setIntent(int, java.lang.String, android.content.Intent); method public void setLabelFor(int, int); + method public void setLightBackgroundLayoutId(int); method public void setLong(int, java.lang.String, long); method public void setOnClickFillInIntent(int, android.content.Intent); method public void setOnClickPendingIntent(int, android.app.PendingIntent); @@ -55711,6 +55960,7 @@ package android.widget { method public java.lang.CharSequence getText(); method public android.view.textclassifier.TextClassifier getTextClassifier(); method public final android.content.res.ColorStateList getTextColors(); + method public android.graphics.drawable.Drawable getTextCursorDrawable(); method public android.text.TextDirectionHeuristic getTextDirectionHeuristic(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); @@ -55841,6 +56091,8 @@ package android.widget { method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public void setTextColor(int); method public void setTextColor(android.content.res.ColorStateList); + method public void setTextCursorDrawable(android.graphics.drawable.Drawable); + method public void setTextCursorDrawable(int); method public void setTextIsSelectable(boolean); method public final void setTextKeepState(java.lang.CharSequence); method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType); diff --git a/api/system-current.txt b/api/system-current.txt index 0f054841c669..0b2871325e13 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -57,6 +57,7 @@ package android { field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL"; field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"; + field public static final java.lang.String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"; field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION"; field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE"; field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"; @@ -2927,6 +2928,20 @@ package android.media { method public void stop(); } + public static class SubtitleData.Builder { + ctor public SubtitleData.Builder(); + ctor public SubtitleData.Builder(android.media.SubtitleData); + method public android.media.SubtitleData build(); + method public android.media.SubtitleData.Builder setSubtitleData(int, long, long, byte[]); + } + + public static class TimedMetaData.Builder { + ctor public TimedMetaData.Builder(); + ctor public TimedMetaData.Builder(android.media.TimedMetaData); + method public android.media.TimedMetaData build(); + method public android.media.TimedMetaData.Builder setTimedMetaData(int, byte[]); + } + } package android.media.audiopolicy { @@ -3014,6 +3029,7 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { + method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int); method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler); method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler); } @@ -3890,14 +3906,6 @@ package android.net.wifi.aware { } -package android.net.wifi.p2p { - - public class WifiP2pManager { - method public void factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); - } - -} - package android.net.wifi.rtt { public static final class RangingRequest.Builder { @@ -4998,6 +5006,7 @@ package android.service.intelligence { } public final class FillRequest { + method public android.view.autofill.AutofillValue getFocusedAutofillValue(); method public android.view.autofill.AutofillId getFocusedId(); method public android.service.intelligence.PresentationParams getPresentationParams(); method public android.service.intelligence.InteractionSessionId getSessionId(); @@ -5786,6 +5795,8 @@ package android.telephony { method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); method public void requestEmbeddedSubscriptionInfoListRefresh(); + method public void setDefaultDataSubId(int); + method public void setDefaultSmsSubId(int); method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>); @@ -7360,22 +7371,8 @@ package android.webkit { ctor public SslErrorHandler(); } - public abstract class TokenBindingService { + public abstract deprecated class TokenBindingService { ctor public TokenBindingService(); - method public abstract void deleteAllKeys(android.webkit.ValueCallback<java.lang.Boolean>); - method public abstract void deleteKey(android.net.Uri, android.webkit.ValueCallback<java.lang.Boolean>); - method public abstract void enableTokenBinding(); - method public static android.webkit.TokenBindingService getInstance(); - method public abstract void getKey(android.net.Uri, java.lang.String[], android.webkit.ValueCallback<android.webkit.TokenBindingService.TokenBindingKey>); - field public static final java.lang.String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256"; - field public static final java.lang.String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5"; - field public static final java.lang.String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS"; - } - - public static abstract class TokenBindingService.TokenBindingKey { - ctor public TokenBindingService.TokenBindingKey(); - method public abstract java.lang.String getAlgorithm(); - method public abstract java.security.KeyPair getKeyPair(); } public class WebChromeClient { @@ -7505,7 +7502,7 @@ package android.webkit { method public abstract android.webkit.GeolocationPermissions getGeolocationPermissions(); method public abstract android.webkit.ServiceWorkerController getServiceWorkerController(); method public abstract android.webkit.WebViewFactoryProvider.Statics getStatics(); - method public abstract android.webkit.TokenBindingService getTokenBindingService(); + method public abstract deprecated android.webkit.TokenBindingService getTokenBindingService(); method public abstract android.webkit.TracingController getTracingController(); method public abstract android.webkit.WebIconDatabase getWebIconDatabase(); method public abstract android.webkit.WebStorage getWebStorage(); diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-greylist-max-p.txt index e69de29bb2d1..e69de29bb2d1 100644 --- a/config/hiddenapi-max-sdk-p-blacklist.txt +++ b/config/hiddenapi-greylist-max-p.txt diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-greylist.txt index e10f72913d22..579ef938fb1d 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -51,6 +51,7 @@ Landroid/app/backup/IBackupManager;->setBackupEnabled(Z)V Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V Landroid/app/backup/IRestoreObserver$Stub;-><init>()V Landroid/app/DownloadManager;->restartDownload([J)V +Landroid/app/IActivityController$Stub;-><init>()V Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration; Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I @@ -62,25 +63,35 @@ Landroid/app/IActivityManager$Stub$Proxy;->updatePersistentConfiguration(Landroi Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager; Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I +Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V +Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I +Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z Landroid/app/IActivityManager;->finishHeavyWeightApp()V Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V Landroid/app/IActivityManager;->getAllStackInfos()Ljava/util/List; Landroid/app/IActivityManager;->getConfiguration()Landroid/content/res/Configuration; +Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo; +Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List; Landroid/app/IActivityManager;->getIntentForIntentSender(Landroid/content/IIntentSender;)Landroid/content/Intent; Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender; Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String; Landroid/app/IActivityManager;->getLaunchedFromUid(Landroid/os/IBinder;)I +Landroid/app/IActivityManager;->getLockTaskModeState()I Landroid/app/IActivityManager;->getMemoryInfo(Landroid/app/ActivityManager$MemoryInfo;)V Landroid/app/IActivityManager;->getPackageProcessState(Ljava/lang/String;Ljava/lang/String;)I Landroid/app/IActivityManager;->getProcessLimit()I +Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo; Landroid/app/IActivityManager;->getProcessPss([I)[J Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String; +Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice; +Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List; Landroid/app/IActivityManager;->getServices(II)Ljava/util/List; Landroid/app/IActivityManager;->getTaskBounds(I)Landroid/graphics/Rect; Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I +Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot; Landroid/app/IActivityManager;->handleApplicationStrictModeViolation(Landroid/os/IBinder;ILandroid/os/StrictMode$ViolationInfo;)V Landroid/app/IActivityManager;->hang(Landroid/os/IBinder;Z)V Landroid/app/IActivityManager;->isInLockTaskMode()Z @@ -98,9 +109,11 @@ Landroid/app/IActivityManager;->profileControl(Ljava/lang/String;IZLandroid/app/ Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V Landroid/app/IActivityManager;->registerProcessObserver(Landroid/app/IProcessObserver;)V Landroid/app/IActivityManager;->registerReceiver(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/IIntentReceiver;Landroid/content/IntentFilter;Ljava/lang/String;II)Landroid/content/Intent; +Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V Landroid/app/IActivityManager;->registerUserSwitchObserver(Landroid/app/IUserSwitchObserver;Ljava/lang/String;)V Landroid/app/IActivityManager;->removeContentProviderExternal(Ljava/lang/String;Landroid/os/IBinder;)V Landroid/app/IActivityManager;->removeStack(I)V +Landroid/app/IActivityManager;->removeTask(I)Z Landroid/app/IActivityManager;->requestBugReport(I)V Landroid/app/IActivityManager;->resizeDockedStack(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;)V Landroid/app/IActivityManager;->resizeStack(ILandroid/graphics/Rect;ZZZI)V @@ -120,8 +133,12 @@ Landroid/app/IActivityManager;->setProcessMemoryTrimLevel(Ljava/lang/String;II)Z Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V Landroid/app/IActivityManager;->setTaskResizeable(II)V Landroid/app/IActivityManager;->shutdown(I)Z +Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I +Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I +Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I Landroid/app/IActivityManager;->startBinderTracking()Z Landroid/app/IActivityManager;->startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;ILandroid/os/Bundle;Landroid/app/IInstrumentationWatcher;Landroid/app/IUiAutomationConnection;ILjava/lang/String;)Z +Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V Landroid/app/IActivityManager;->startSystemLockTaskMode(I)V Landroid/app/IActivityManager;->startUserInBackground(I)Z Landroid/app/IActivityManager;->stopAppSwitches()V @@ -144,12 +161,16 @@ Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo; Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V +Landroid/app/IAlarmManager;->setTime(J)Z Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo; +Landroid/app/IAssistDataReceiver$Stub;-><init>()V +Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V +Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z Landroid/app/IInstrumentationWatcher$Stub;-><init>()V Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher; @@ -236,6 +257,7 @@ Landroid/bluetooth/IBluetooth;->fetchRemoteUuids(Landroid/bluetooth/BluetoothDev Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String; Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String; Landroid/bluetooth/IBluetooth;->isEnabled()Z +Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp; Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z @@ -266,6 +288,10 @@ Landroid/bluetooth/IBluetoothManager;->unregisterStateChangeCallback(Landroid/bl Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap; Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V +Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V +Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V +Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V +Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder; Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard; @@ -312,11 +338,13 @@ Landroid/content/om/IOverlayManager;->getOverlayInfo(Ljava/lang/String;I)Landroi Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V +Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver; Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String; Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver; Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String; Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I @@ -325,6 +353,7 @@ Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBi Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V Landroid/content/pm/IPackageDeleteObserver2$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver2; Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V +Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V Landroid/content/pm/IPackageInstaller;->uninstall(Landroid/content/pm/VersionedPackage;Ljava/lang/String;ILandroid/content/IntentSender;I)V Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder; @@ -364,11 +393,14 @@ Landroid/content/pm/IPackageManager;->checkUidSignatures(II)I Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String; Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V +Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo; Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I +Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String; Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I Landroid/content/pm/IPackageManager;->getFlagsForUid(I)I +Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName; Landroid/content/pm/IPackageManager;->getInstalledApplications(II)Landroid/content/pm/ParceledListSlice; Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice; Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String; @@ -376,6 +408,7 @@ Landroid/content/pm/IPackageManager;->getInstallLocation()I Landroid/content/pm/IPackageManager;->getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo; Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo; Landroid/content/pm/IPackageManager;->getNameForUid(I)Ljava/lang/String; +Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo; Landroid/content/pm/IPackageManager;->getPackageInstaller()Landroid/content/pm/IPackageInstaller; Landroid/content/pm/IPackageManager;->getPackagesForUid(I)[Ljava/lang/String; Landroid/content/pm/IPackageManager;->getPackageUid(Ljava/lang/String;II)I @@ -415,6 +448,7 @@ Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver; Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String; Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I +Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService; Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V @@ -438,6 +472,7 @@ Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)La Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V +Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager; @@ -447,7 +482,10 @@ Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landr Landroid/location/ICountryListener$Stub;-><init>()V Landroid/location/IGeocodeProvider$Stub;-><init>()V Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider; +Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; +Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; Landroid/location/IGeofenceProvider$Stub;-><init>()V +Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/location/ILocationListener$Stub;-><init>()V @@ -460,6 +498,10 @@ Landroid/location/ILocationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager; Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List; +Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String; +Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V +Landroid/location/INetInitiatedListener$Stub;-><init>()V +Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String; Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String; @@ -486,6 +528,17 @@ Landroid/media/MediaFile;-><init>()V Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager; +Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V +Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V +Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V +Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V +Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V +Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V +Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V +Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V +Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V +Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V +Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties; Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo; @@ -499,6 +552,7 @@ Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landro Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties; Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo; Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo; +Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState; Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo; Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String; @@ -508,9 +562,12 @@ Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->reportInetCondition(II)V Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V +Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd; +Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyListener$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; +Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo; Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V @@ -520,14 +577,19 @@ Landroid/net/INetworkPolicyManager;->snoozeLimit(Landroid/net/NetworkTemplate;)V Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService; Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; +Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService; Landroid/net/INetworkStatsService;->forceUpdate()V Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats; Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String; Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession; Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession; Landroid/net/INetworkStatsSession;->close()V +Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory; +Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory; Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats; Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; +Landroid/net/InterfaceConfiguration;-><init>()V +Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange; Landroid/net/MobileLinkQualityInfo;-><init>()V Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager; Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger; @@ -552,7 +614,12 @@ Landroid/nfc/INfcAdapterExtras;->getDriverName(Ljava/lang/String;)Ljava/lang/Str Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle; Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle; +Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V +Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable; Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult; +Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult; +Landroid/os/AsyncResult;->result:Ljava/lang/Object; +Landroid/os/AsyncResult;->userObj:Ljava/lang/Object; Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask; Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status; Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean; @@ -569,18 +636,24 @@ Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_CURRENT:Ljava/lang/String; Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String; Landroid/os/BatteryStats$Counter;-><init>()V Landroid/os/BatteryStats$Counter;->getCountLocked(I)I +Landroid/os/BatteryStats$HistoryItem;-><init>()V Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B +Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C Landroid/os/BatteryStats$HistoryItem;->clear()V +Landroid/os/BatteryStats$HistoryItem;->cmd:B Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem; Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V Landroid/os/BatteryStats$HistoryItem;->states2:I +Landroid/os/BatteryStats$HistoryItem;->states:I +Landroid/os/BatteryStats$HistoryItem;->time:J Landroid/os/BatteryStats$Timer;-><init>()V +Landroid/os/BatteryStats$Timer;->getCountLocked(I)I Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I @@ -604,6 +677,8 @@ Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer; Landroid/os/BatteryStats$Uid$Sensor;->GPS:I Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V +Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer; +Landroid/os/BatteryStats$Uid;-><init>()V Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer; Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J Landroid/os/BatteryStats$Uid;->getMobileRadioActiveTime(I)J @@ -616,6 +691,7 @@ Landroid/os/BatteryStats$Uid;->getVideoTurnedOnTimer()Landroid/os/BatteryStats$T Landroid/os/BatteryStats$Uid;->getWakelockStats()Landroid/util/ArrayMap; Landroid/os/BatteryStats$Uid;->getWifiBatchedScanTime(IJI)J Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J +Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J Landroid/os/BatteryStats;-><init>()V Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J @@ -627,6 +703,7 @@ Landroid/os/BatteryStats;->getBatteryUptime(J)J Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J +Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z Landroid/os/BatteryStats;->getPhoneOnTime(JI)J Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J Landroid/os/BatteryStats;->getScreenBrightnessTime(IJI)J @@ -640,6 +717,10 @@ Landroid/os/BatteryStats;->STATS_CURRENT:I Landroid/os/BatteryStats;->WAKE_TYPE_PARTIAL:I Landroid/os/Binder;->execTransact(IJJI)Z Landroid/os/Binder;->mObject:J +Landroid/os/Broadcaster;-><init>()V +Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V +Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V +Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String; Landroid/os/Build;->getLong(Ljava/lang/String;)J Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String; @@ -713,6 +794,7 @@ Landroid/os/Environment;->buildExternalStorageAppMediaDirs(Ljava/lang/String;)[L Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File; +Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File; Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File; Landroid/os/Environment;->initForCurrentUser()V Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File; @@ -733,12 +815,15 @@ Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z +Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V Landroid/os/Handler;-><init>(Z)V Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger; +Landroid/os/Handler;->getMain()Landroid/os/Handler; Landroid/os/Handler;->getPostMessage(Ljava/lang/Runnable;Ljava/lang/Object;)Landroid/os/Message; Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback; Landroid/os/Handler;->mLooper:Landroid/os/Looper; Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger; +Landroid/os/HwBinder;->reportSyspropChanged()V Landroid/os/HwParcel;-><init>(Z)V Landroid/os/HwRemoteBinder;-><init>()V Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V @@ -749,16 +834,26 @@ Landroid/os/IDeviceIdleController;->addPowerSaveTempWhitelistApp(Ljava/lang/Stri Landroid/os/IDeviceIdleController;->getAppIdTempWhitelist()[I Landroid/os/IDeviceIdleController;->getFullPowerWhitelistExceptIdle()[Ljava/lang/String; Landroid/os/INetworkManagementService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService; +Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V +Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V Landroid/os/INetworkManagementService;->disableNat(Ljava/lang/String;Ljava/lang/String;)V +Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V Landroid/os/INetworkManagementService;->enableNat(Ljava/lang/String;Ljava/lang/String;)V Landroid/os/INetworkManagementService;->getInterfaceConfig(Ljava/lang/String;)Landroid/net/InterfaceConfiguration; Landroid/os/INetworkManagementService;->getIpForwardingEnabled()Z +Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z Landroid/os/INetworkManagementService;->isTetheringStarted()Z Landroid/os/INetworkManagementService;->listTetheredInterfaces()[Ljava/lang/String; +Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V +Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V +Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V Landroid/os/INetworkManagementService;->setIpForwardingEnabled(Z)V +Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V Landroid/os/INetworkManagementService;->startTethering([Ljava/lang/String;)V Landroid/os/INetworkManagementService;->stopTethering()V Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V +Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController; @@ -767,12 +862,15 @@ Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager; Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I +Landroid/os/IPowerManager;->goToSleep(JII)V Landroid/os/IPowerManager;->isInteractive()Z Landroid/os/IPowerManager;->nap(J)V +Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V Landroid/os/IPowerManager;->userActivity(JII)V Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem; +Landroid/os/IRemoteCallback$Stub;-><init>()V Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder; @@ -798,6 +896,7 @@ Landroid/os/Message;->flags:I Landroid/os/Message;->markInUse()V Landroid/os/Message;->next:Landroid/os/Message; Landroid/os/Message;->recycleUnchecked()V +Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message; Landroid/os/Message;->target:Landroid/os/Handler; Landroid/os/Message;->toString(J)Ljava/lang/String; Landroid/os/Message;->when:J @@ -817,13 +916,16 @@ Landroid/os/Parcel;->mCreators:Ljava/util/HashMap; Landroid/os/Parcel;->mNativePtr:J Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet; +Landroid/os/Parcel;->readBlob()[B Landroid/os/Parcel;->readCharSequence()Ljava/lang/CharSequence; Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable; Landroid/os/Parcel;->readExceptionCode()I Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator; Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor; +Landroid/os/Parcel;->readStringArray()[Ljava/lang/String; Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V +Landroid/os/Parcel;->writeBlob([B)V Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V @@ -890,15 +992,27 @@ Landroid/os/Process;->SHELL_UID:I Landroid/os/Process;->VPN_UID:I Landroid/os/Process;->WIFI_UID:I Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z +Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V +Landroid/os/Registrant;->clear()V Landroid/os/Registrant;->getHandler()Landroid/os/Handler; Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message; +Landroid/os/Registrant;->notifyRegistrant()V +Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V Landroid/os/Registrant;->notifyResult(Ljava/lang/Object;)V +Landroid/os/RegistrantList;-><init>()V Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V +Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V +Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V Landroid/os/RegistrantList;->get(I)Ljava/lang/Object; +Landroid/os/RegistrantList;->notifyRegistrants()V +Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V +Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V +Landroid/os/RegistrantList;->removeCleared()V Landroid/os/RegistrantList;->size()I Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler; Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap; +Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException; Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z Landroid/os/SELinux;->getContext()Ljava/lang/String; Landroid/os/SELinux;->getFileContext(Ljava/lang/String;)Ljava/lang/String; @@ -920,6 +1034,7 @@ Landroid/os/ServiceManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/os/ Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder; Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V +Landroid/os/ServiceSpecificException;->errorCode:I Landroid/os/SharedMemory;->getFd()I Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String; Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs; @@ -927,6 +1042,7 @@ Landroid/os/storage/IObbActionListener$Stub;-><init>()V Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener; Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager; +Landroid/os/storage/StorageEventListener;-><init>()V Landroid/os/StrictMode$Span;->finish()V Landroid/os/StrictMode$ThreadPolicy;->mask:I Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I @@ -954,8 +1070,12 @@ Landroid/os/SystemProperties;->native_get_int(Ljava/lang/String;I)I Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V Landroid/os/SystemProperties;->PROP_NAME_MAX:I +Landroid/os/SystemProperties;->reportSyspropChanged()V Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList; Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V +Landroid/os/SystemService;->start(Ljava/lang/String;)V +Landroid/os/SystemService;->stop(Ljava/lang/String;)V +Landroid/os/SystemVibrator;-><init>()V Landroid/os/SystemVibrator;-><init>(Landroid/content/Context;)V Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V @@ -994,6 +1114,7 @@ Landroid/os/UserHandle;->getCallingUserId()I Landroid/os/UserHandle;->getUid(II)I Landroid/os/UserHandle;->getUserId(I)I Landroid/os/UserHandle;->isIsolated(I)Z +Landroid/os/UserHandle;->isSameApp(II)Z Landroid/os/UserHandle;->mHandle:I Landroid/os/UserHandle;->MU_ENABLED:Z Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle; @@ -1023,6 +1144,7 @@ Landroid/os/UserManager;->getUserSerialNumber(I)I Landroid/os/UserManager;->getUserStartRealtime()J Landroid/os/UserManager;->getUserUnlockRealtime()J Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z +Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z Landroid/os/UserManager;->isGuestUser(I)Z Landroid/os/UserManager;->isLinkedUser()Z @@ -1072,6 +1194,7 @@ Landroid/R$styleable;->CalendarView_unfocusedMonthDateColor:I Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I Landroid/R$styleable;->CalendarView_weekNumberColor:I Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I +Landroid/R$styleable;->CheckBoxPreference:[I Landroid/R$styleable;->CheckedTextView:[I Landroid/R$styleable;->CheckedTextView_checkMark:I Landroid/R$styleable;->CompoundButton:[I @@ -1382,7 +1505,18 @@ Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V Landroid/telephony/CarrierMessagingServiceManager;-><init>()V +Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V +Landroid/telephony/ims/compat/ImsService;-><init>()V +Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V +Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V +Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V +Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V +Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService; +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri; +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I Landroid/telephony/SmsCbCmasInfo;->getCategory()I Landroid/telephony/SmsCbCmasInfo;->getCertainty()I Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I @@ -1424,10 +1558,21 @@ Landroid/view/accessibility/IAccessibilityManager;->getEnabledAccessibilityServi Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager; +Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V Landroid/view/IDockedStackListener$Stub;-><init>()V Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats; +Landroid/view/IRecentsAnimationController;->finish(Z)V +Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot; Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V +Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V +Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V +Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V +Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V +Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V +Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V +Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V +Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V Landroid/view/IRotationWatcher$Stub;-><init>()V Landroid/view/IWindow$Stub;-><init>()V Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow; @@ -1439,6 +1584,9 @@ Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I Landroid/view/IWindowManager$Stub;-><init>()V Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager; +Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V +Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z +Landroid/view/IWindowManager;->endProlongedAnimations()V Landroid/view/IWindowManager;->executeAppTransition()V Landroid/view/IWindowManager;->freezeRotation(I)V Landroid/view/IWindowManager;->getAnimationScale(I)F @@ -1447,16 +1595,20 @@ Landroid/view/IWindowManager;->getBaseDisplaySize(ILandroid/graphics/Point;)V Landroid/view/IWindowManager;->getDockedStackSide()I Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V +Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V Landroid/view/IWindowManager;->hasNavigationBar(I)Z Landroid/view/IWindowManager;->isKeyguardLocked()Z Landroid/view/IWindowManager;->isKeyguardSecure()Z Landroid/view/IWindowManager;->isSafeModeEnabled()Z Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V +Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V +Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V Landroid/view/IWindowManager;->setAnimationScale(IF)V Landroid/view/IWindowManager;->setAnimationScales([F)V Landroid/view/IWindowManager;->setInTouchMode(Z)V +Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V Landroid/view/IWindowManager;->setShelfHeight(ZI)V Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V Landroid/view/IWindowManager;->showStrictModeViolation(Z)V @@ -1496,6 +1648,7 @@ Lcom/android/ims/ImsCall;->isMultiparty()Z Lcom/android/ims/ImsCall;->reject(I)V Lcom/android/ims/ImsCall;->terminate(I)V Lcom/android/ims/ImsConfigListener$Stub;-><init>()V +Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig; Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager; @@ -1505,12 +1658,48 @@ Lcom/android/ims/ImsManager;->isVolteEnabledByPlatform(Landroid/content/Context; Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession; +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V +Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V +Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V +Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V Lcom/android/ims/internal/IImsService$Stub;-><init>()V Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService; Lcom/android/ims/internal/IImsUt$Stub;-><init>()V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V +Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V @@ -1521,7 +1710,52 @@ Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(I Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V +Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V +Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V +Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V +Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V +Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V +Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; +Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V +Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V +Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V +Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I +Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I +Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V +Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V +Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService; +Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService; +Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z +Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z +Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z +Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z Lcom/android/internal/app/AlertActivity;-><init>()V Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController; Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams; @@ -1552,6 +1786,7 @@ Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_setUserRestrictions: Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I +Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List; Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List; Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V @@ -1599,6 +1834,7 @@ Lcom/android/internal/appwidget/IAppWidgetService;->bindRemoteViewsService(Ljava Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews; Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V +Lcom/android/internal/content/PackageMonitor;-><init>()V Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor; Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor; @@ -1612,6 +1848,14 @@ Lcom/android/internal/location/GpsNetInitiatedHandler;->decodeString(Ljava/lang/ Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V +Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider; +Lcom/android/internal/location/ILocationProvider;->disable()V +Lcom/android/internal/location/ILocationProvider;->enable()V +Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties; +Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I +Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J +Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z +Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V Lcom/android/internal/logging/MetricsLogger;-><init>()V Lcom/android/internal/net/LegacyVpnInfo;-><init>()V Lcom/android/internal/net/VpnConfig;-><init>()V @@ -1981,6 +2225,7 @@ Lcom/android/internal/R$styleable;->MapView_apiKey:I Lcom/android/internal/R$styleable;->MenuGroup:[I Lcom/android/internal/R$styleable;->MenuItem:[I Lcom/android/internal/R$styleable;->MenuView:[I +Lcom/android/internal/R$styleable;->NumberPicker:[I Lcom/android/internal/R$styleable;->PopupWindow:[I Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I Lcom/android/internal/R$styleable;->PopupWindow_popupBackground:I @@ -2106,6 +2351,7 @@ Lcom/android/internal/R$styleable;->TextView_textStyle:I Lcom/android/internal/R$styleable;->TextView_typeface:I Lcom/android/internal/R$styleable;->TextView_width:I Lcom/android/internal/R$styleable;->Theme:[I +Lcom/android/internal/R$styleable;->TwoLineListItem:[I Lcom/android/internal/R$styleable;->View:[I Lcom/android/internal/R$styleable;->ViewAnimator:[I Lcom/android/internal/R$styleable;->ViewFlipper:[I @@ -3126,6 +3372,7 @@ Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I Lcom/android/internal/telephony/ITelephony;->getCallState()I Lcom/android/internal/telephony/ITelephony;->getDataActivity()I +Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z Lcom/android/internal/telephony/ITelephony;->getDataState()I Lcom/android/internal/telephony/ITelephony;->getNetworkType()I Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt deleted file mode 100644 index a21fa3063f17..000000000000 --- a/config/hiddenapi-vendor-list.txt +++ /dev/null @@ -1,247 +0,0 @@ -Landroid/app/IActivityController$Stub;-><init>()V -Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V -Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V -Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V -Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo; -Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List; -Landroid/app/IActivityManager;->getLockTaskModeState()I -Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo; -Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice; -Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List; -Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot; -Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V -Landroid/app/IActivityManager;->removeTask(I)Z -Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I -Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I -Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I -Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V -Landroid/app/IAlarmManager;->setTime(J)Z -Landroid/app/IAssistDataReceiver$Stub;-><init>()V -Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V -Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V -Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V -Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V -Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V -Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V -Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V -Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V -Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V -Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V -Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo; -Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; -Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName; -Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo; -Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V -Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V -Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; -Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; -Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V -Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String; -Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V -Landroid/location/INetInitiatedListener$Stub;-><init>()V -Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z -Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V -Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V -Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V -Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V -Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState; -Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd; -Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V -Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo; -Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService; -Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory; -Landroid/net/InterfaceConfiguration;-><init>()V -Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange; -Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V -Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable; -Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult; -Landroid/os/AsyncResult;->result:Ljava/lang/Object; -Landroid/os/AsyncResult;->userObj:Ljava/lang/Object; -Landroid/os/BatteryStats$HistoryItem;-><init>()V -Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B -Landroid/os/BatteryStats$HistoryItem;->cmd:B -Landroid/os/BatteryStats$HistoryItem;->states:I -Landroid/os/BatteryStats$HistoryItem;->time:J -Landroid/os/BatteryStats$Timer;->getCountLocked(I)I -Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer; -Landroid/os/BatteryStats$Uid;-><init>()V -Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J -Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z -Landroid/os/Broadcaster;-><init>()V -Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V -Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V -Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V -Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File; -Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V -Landroid/os/Handler;->getMain()Landroid/os/Handler; -Landroid/os/HwBinder;->reportSyspropChanged()V -Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService; -Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V -Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V -Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V -Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z -Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V -Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V -Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V -Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V -Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V -Landroid/os/IPowerManager;->goToSleep(JII)V -Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V -Landroid/os/IRemoteCallback$Stub;-><init>()V -Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message; -Landroid/os/Parcel;->readBlob()[B -Landroid/os/Parcel;->readStringArray()[Ljava/lang/String; -Landroid/os/Parcel;->writeBlob([B)V -Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V -Landroid/os/Registrant;->clear()V -Landroid/os/Registrant;->notifyRegistrant()V -Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V -Landroid/os/RegistrantList;-><init>()V -Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V -Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V -Landroid/os/RegistrantList;->notifyRegistrants()V -Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V -Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V -Landroid/os/RegistrantList;->removeCleared()V -Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException; -Landroid/os/ServiceSpecificException;->errorCode:I -Landroid/os/storage/StorageEventListener;-><init>()V -Landroid/os/SystemProperties;->reportSyspropChanged()V -Landroid/os/SystemService;->start(Ljava/lang/String;)V -Landroid/os/SystemService;->stop(Ljava/lang/String;)V -Landroid/os/SystemVibrator;-><init>()V -Landroid/os/UserHandle;->isSameApp(II)Z -Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z -Landroid/R$styleable;->CheckBoxPreference:[I -Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V -Landroid/telephony/ims/compat/ImsService;-><init>()V -Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V -Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V -Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V -Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V -Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService; -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri; -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I -Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V -Landroid/view/IRecentsAnimationController;->finish(Z)V -Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot; -Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V -Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V -Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V -Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V -Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V -Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V -Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V -Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V -Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V -Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z -Landroid/view/IWindowManager;->endProlongedAnimations()V -Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V -Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V -Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V -Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V -Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V -Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V -Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V -Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V -Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V -Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V -Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V -Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I -Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I -Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V -Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V -Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService; -Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService; -Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z -Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V -Lcom/android/internal/content/PackageMonitor;-><init>()V -Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider; -Lcom/android/internal/location/ILocationProvider;->disable()V -Lcom/android/internal/location/ILocationProvider;->enable()V -Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties; -Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I -Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J -Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z -Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V -Lcom/android/internal/R$styleable;->NumberPicker:[I -Lcom/android/internal/R$styleable;->TwoLineListItem:[I -Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6f0b6c8687db..c7a9d99fe927 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1731,8 +1731,11 @@ class ContextImpl extends Context { throw new IllegalArgumentException("connection is null"); } if (mPackageInfo != null) { - IServiceConnection sd = mPackageInfo.forgetServiceDispatcher( - getOuterContext(), conn); + IServiceConnection sd = mPackageInfo.lookupServiceDispatcher(conn, getOuterContext()); + if (sd == null) { + throw new IllegalArgumentException("ServiceConnection not currently bound: " + + conn); + } try { ActivityManager.getService().updateServiceGroup(sd, group, importance); } catch (RemoteException e) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 3f1075448c84..759763b4199a 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -696,6 +696,23 @@ public final class LoadedApk { return loaders; } + private StrictMode.ThreadPolicy allowThreadDiskReads() { + if (mActivityThread == null) { + // When LoadedApk is used without an ActivityThread (usually in a + // zygote context), don't call into StrictMode, as it initializes + // the binder subsystem, which we don't want. + return null; + } + + return StrictMode.allowThreadDiskReads(); + } + + private void setThreadPolicy(StrictMode.ThreadPolicy policy) { + if (mActivityThread != null && policy != null) { + StrictMode.setThreadPolicy(policy); + } + } + private void createOrUpdateClassLoaderLocked(List<String> addedPaths) { if (mPackageName.equals("android")) { // Note: This branch is taken for system server and we don't need to setup @@ -718,8 +735,11 @@ public final class LoadedApk { // Avoid the binder call when the package is the current application package. // The activity manager will perform ensure that dexopt is performed before - // spinning up the process. - if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) { + // spinning up the process. Similarly, don't call into binder when we don't + // have an ActivityThread object. + if (mActivityThread != null + && !Objects.equals(mPackageName, ActivityThread.currentPackageName()) + && mIncludeCode) { try { ActivityThread.getPackageManager().notifyPackageUse(mPackageName, PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE); @@ -790,12 +810,12 @@ public final class LoadedApk { // mIncludeCode == false). if (!mIncludeCode) { if (mDefaultClassLoader == null) { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader( "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, mBaseClassLoader, null /* classLoaderName */); - StrictMode.setThreadPolicy(oldPolicy); + setThreadPolicy(oldPolicy); mAppComponentFactory = AppComponentFactory.DEFAULT; } @@ -822,7 +842,7 @@ public final class LoadedApk { if (mDefaultClassLoader == null) { // Temporarily disable logging of disk reads on the Looper thread // as this is early and necessary. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders( mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath, @@ -834,18 +854,18 @@ public final class LoadedApk { mApplicationInfo.classLoaderName, sharedLibraries); mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader); - StrictMode.setThreadPolicy(oldPolicy); + setThreadPolicy(oldPolicy); // Setup the class loader paths for profiling. needToSetupJitProfiles = true; } if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) { // Temporarily disable logging of disk reads on the Looper thread as this is necessary - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); try { ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths); } finally { - StrictMode.setThreadPolicy(oldPolicy); + setThreadPolicy(oldPolicy); } } @@ -879,11 +899,11 @@ public final class LoadedApk { extraLibPaths.add("/product/lib" + abiSuffix); } if (!extraLibPaths.isEmpty()) { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); try { ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths); } finally { - StrictMode.setThreadPolicy(oldPolicy); + setThreadPolicy(oldPolicy); } } @@ -1639,6 +1659,19 @@ public final class LoadedApk { } } + @UnsupportedAppUsage + public IServiceConnection lookupServiceDispatcher(ServiceConnection c, + Context context) { + synchronized (mServices) { + LoadedApk.ServiceDispatcher sd = null; + ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context); + if (map != null) { + sd = map.get(c); + } + return sd != null ? sd.getIServiceConnection() : null; + } + } + public final IServiceConnection forgetServiceDispatcher(Context context, ServiceConnection c) { synchronized (mServices) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0281e6a0631e..aa1b5af9b112 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8263,7 +8263,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); - remoteViews.setReapplyDisallowed(); + remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); } // also update the end margin if there is an image Resources resources = mBuilder.mContext.getResources(); @@ -8394,7 +8394,7 @@ public class Notification implements Parcelable customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams)); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); - remoteViews.setReapplyDisallowed(); + remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); } return remoteViews; } diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 32fc0dcebbc8..9dcd1228522b 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -119,6 +119,7 @@ public final class StatsManager { /** * @deprecated Use {@link #addConfig(long, byte[])} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean addConfiguration(long configKey, byte[] config) { try { @@ -154,6 +155,7 @@ public final class StatsManager { /** * @deprecated Use {@link #removeConfig(long)} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean removeConfiguration(long configKey) { try { @@ -222,6 +224,7 @@ public final class StatsManager { /** * @deprecated Use {@link #setBroadcastSubscriber(PendingIntent, long, long)} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean setBroadcastSubscriber( long configKey, long subscriberId, PendingIntent pendingIntent) { @@ -275,6 +278,7 @@ public final class StatsManager { /** * @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) { try { @@ -312,6 +316,7 @@ public final class StatsManager { /** * @deprecated Use {@link #getReports(long)} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public @Nullable byte[] getData(long configKey) { try { @@ -348,6 +353,7 @@ public final class StatsManager { /** * @deprecated Use {@link #getStatsMetadata()} */ + @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public @Nullable byte[] getMetadata() { try { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index dfe371c3248e..0404e80df9e2 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -59,6 +59,7 @@ import android.hardware.SystemSensorManager; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.IBiometricService; import android.hardware.camera2.CameraManager; +import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; @@ -386,6 +387,14 @@ final class SystemServiceRegistry { return new DisplayManager(ctx.getOuterContext()); }}); + registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class, + new CachedServiceFetcher<ColorDisplayManager>() { + @Override + public ColorDisplayManager createService(ContextImpl ctx) { + return new ColorDisplayManager(); + } + }); + // InputMethodManager has its own cache strategy based on display id to support apps that // still assume InputMethodManager is a per-process singleton and it's safe to directly // access internal fields via reflection. Hence directly use ServiceFetcher instead of diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 78b7d48165ec..257122d3abe0 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -75,6 +75,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** The current windowing mode of the configuration. */ private @WindowingMode int mWindowingMode; + /** The display windowing mode of the configuration */ + private @WindowingMode int mDisplayWindowingMode; + /** Windowing mode is currently not defined. */ public static final int WINDOWING_MODE_UNDEFINED = 0; /** Occupies the full area of the screen or the parent container. */ @@ -176,6 +179,10 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** Bit that indicates that the {@link #mRotation} changed. * @hide */ public static final int WINDOW_CONFIG_ROTATION = 1 << 5; + /** Bit that indicates that the {@link #mDisplayWindowingMode} changed. + * @hide */ + public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 6; + /** @hide */ @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = { WINDOW_CONFIG_BOUNDS, @@ -184,6 +191,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu WINDOW_CONFIG_ACTIVITY_TYPE, WINDOW_CONFIG_ALWAYS_ON_TOP, WINDOW_CONFIG_ROTATION, + WINDOW_CONFIG_DISPLAY_WINDOWING_MODE, }) public @interface WindowConfig {} @@ -211,6 +219,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu dest.writeInt(mActivityType); dest.writeInt(mAlwaysOnTop); dest.writeInt(mRotation); + dest.writeInt(mDisplayWindowingMode); } private void readFromParcel(Parcel source) { @@ -220,6 +229,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu mActivityType = source.readInt(); mAlwaysOnTop = source.readInt(); mRotation = source.readInt(); + mDisplayWindowingMode = source.readInt(); } @Override @@ -322,6 +332,12 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu return mWindowingMode; } + /** @hide */ + public void setDisplayWindowingMode(@WindowingMode int windowingMode) { + mDisplayWindowingMode = windowingMode; + } + + public void setActivityType(@ActivityType int activityType) { if (mActivityType == activityType) { return; @@ -351,6 +367,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu setActivityType(other.mActivityType); setAlwaysOnTop(other.mAlwaysOnTop); setRotation(other.mRotation); + setDisplayWindowingMode(other.mDisplayWindowingMode); } /** Set this object to completely undefined. @@ -367,6 +384,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu setActivityType(ACTIVITY_TYPE_UNDEFINED); setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED); setRotation(ROTATION_UNDEFINED); + setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED); } /** @@ -407,6 +425,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu changed |= WINDOW_CONFIG_ROTATION; setRotation(delta.mRotation); } + if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED + && mDisplayWindowingMode != delta.mDisplayWindowingMode) { + changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE; + setDisplayWindowingMode(delta.mDisplayWindowingMode); + } return changed; } @@ -455,6 +478,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu changes |= WINDOW_CONFIG_ROTATION; } + if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED) + && mDisplayWindowingMode != other.mDisplayWindowingMode) { + changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE; + } + return changes; } @@ -493,6 +521,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu if (n != 0) return n; n = mRotation - that.mRotation; if (n != 0) return n; + n = mDisplayWindowingMode - that.mDisplayWindowingMode; + if (n != 0) return n; // if (n != 0) return n; return n; @@ -522,6 +552,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu result = 31 * result + mActivityType; result = 31 * result + mAlwaysOnTop; result = 31 * result + mRotation; + result = 31 * result + mDisplayWindowingMode; return result; } @@ -531,6 +562,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu return "{ mBounds=" + mBounds + " mAppBounds=" + mAppBounds + " mWindowingMode=" + windowingModeToString(mWindowingMode) + + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode) + " mActivityType=" + activityTypeToString(mActivityType) + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop) + " mRotation=" + (mRotation == ROTATION_UNDEFINED @@ -603,7 +635,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * @hide */ public boolean hasWindowDecorCaption() { - return mWindowingMode == WINDOWING_MODE_FREEFORM; + return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM + || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM); } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 98d2a406fc0c..3a9728413151 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -10092,11 +10092,40 @@ public class DevicePolicyManager { if (mService != null) { try { return mService.isPackageAllowedToAccessCalendarForUser(packageName, - mContext.getUserId()); + myUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return false; } + + /** + * Gets a set of package names that are whitelisted to access cross profile calendar APIs. + * + * <p>To query for a specific user, use + * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for + * that user, and get a {@link DevicePolicyManager} from this context. + * + * @return the set of names of packages that were previously whitelisted via + * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an + * empty set if none have been whitelisted. + * + * @see #addCrossProfileCalendarPackage(ComponentName, String) + * @see #removeCrossProfileCalendarPackage(ComponentName, String) + * @see #getCrossProfileCalendarPackages(ComponentName) + * @hide + */ + public @NonNull Set<String> getCrossProfileCalendarPackages() { + throwIfParentInstance("getCrossProfileCalendarPackages"); + if (mService != null) { + try { + return new ArraySet<>(mService.getCrossProfileCalendarPackagesForUser( + myUserId())); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return Collections.emptySet(); + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 297676d397bf..fcf74ee301d8 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -427,4 +427,5 @@ interface IDevicePolicyManager { boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName); List<String> getCrossProfileCalendarPackages(in ComponentName admin); boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle); + List<String> getCrossProfileCalendarPackagesForUser(int userHandle); } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 318dbee99e5a..c740c42c9c0d 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -88,6 +88,7 @@ public class AppWidgetHostView extends FrameLayout { int mViewMode = VIEW_MODE_NOINIT; int mLayoutId = -1; private OnClickHandler mOnClickHandler; + private boolean mOnLightBackground; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; @@ -374,6 +375,15 @@ public class AppWidgetHostView extends FrameLayout { } /** + * Sets whether the widget should is being displayed on a light/white background and use an + * alternate UI if available. + * @see RemoteViews#setLightBackgroundLayoutId(int) + */ + public void setOnLightBackground(boolean useDarkTextLayout) { + mOnLightBackground = useDarkTextLayout; + } + + /** * Update the AppWidgetProviderInfo for this view, and reset it to the * initial layout. */ @@ -413,6 +423,10 @@ public class AppWidgetHostView extends FrameLayout { mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { + if (mOnLightBackground) { + remoteViews = remoteViews.getDarkTextViews(); + } + if (mAsyncExecutor != null && useAsyncIfPossible) { inflateAsync(remoteViews); return; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 145c92731458..d1017c59b963 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -51,6 +51,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -586,7 +587,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 { private int noteProxyOp(String callingPkg, int op) { if (op != AppOpsManager.OP_NONE) { int mode = mAppOpsManager.noteProxyOp(op, callingPkg); - return mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode; + int nonDefaultMode = mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode; + if (mode == MODE_DEFAULT && nonDefaultMode == MODE_IGNORED) { + Slog.w(TAG, "Denying access for " + callingPkg + " to " + getClass().getName() + + " (" + AppOpsManager.opToName(op) + + " = " + AppOpsManager.opToName(mode) + ")"); + } + return mode == MODE_DEFAULT ? nonDefaultMode : mode; } return AppOpsManager.MODE_ALLOWED; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 68aac642ecb0..e2c7b85b3280 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3003,6 +3003,11 @@ public abstract class Context { * how the process will be managed in some cases based on those flags. Currently only * works on isolated processes (will be ignored for non-isolated processes). * + * <p>Note that this call does not take immediate effect, but will be applied the next + * time the impacted process is adjusted for some other reason. Typically you would + * call this before then calling a new {@link #bindIsolatedService} on the service + * of interest, with that binding causing the process to be shuffled accordingly.</p> + * * @param conn The connection interface previously supplied to bindService(). This * parameter must not be null. * @param group A group to put this connection's process in. Upon calling here, this @@ -3126,6 +3131,7 @@ public abstract class Context { //@hide: HDMI_CONTROL_SERVICE, INPUT_SERVICE, DISPLAY_SERVICE, + //@hide COLOR_DISPLAY_SERVICE, USER_SERVICE, RESTRICTIONS_SERVICE, APP_OPS_SERVICE, @@ -4108,6 +4114,16 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms. + * + * @see #getSystemService(String) + * @see android.hardware.display.ColorDisplayManager + * @hide + */ + public static final String COLOR_DISPLAY_SERVICE = "color_display"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.os.UserManager} for managing users on devices that support multiple users. * * @see #getSystemService(String) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index edfb3a72e933..c2907d2ccc1b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1916,12 +1916,15 @@ public class Intent implements Parcelable, Cloneable { /** * Activity action: Launch UI to review app uses of permissions. * <p> - * Input: Nothing + * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name + * that will be displayed by the launched UI. * </p> * <p> * Output: Nothing. * </p> * + * @see #EXTRA_PERMISSION_NAME + * * @hide */ @SystemApi diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 98a135f96d98..07d6e4785759 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1303,7 +1303,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** {@hide} */ public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) { long token = proto.start(fieldId); - super.writeToProto(proto, ApplicationInfoProto.PACKAGE); + super.writeToProto(proto, ApplicationInfoProto.PACKAGE, dumpFlags); proto.write(ApplicationInfoProto.PERMISSION, permission); proto.write(ApplicationInfoProto.PROCESS_NAME, processName); proto.write(ApplicationInfoProto.UID, uid); diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index cdb781438909..ff7b34773268 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -433,18 +433,18 @@ public class PackageItemInfo { /** * @hide */ - public void writeToProto(ProtoOutputStream proto, long fieldId) { + public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) { long token = proto.start(fieldId); if (name != null) { proto.write(PackageItemInfoProto.NAME, name); } proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName); - if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { - proto.write(PackageItemInfoProto.LABEL_RES, labelRes); + proto.write(PackageItemInfoProto.LABEL_RES, labelRes); + if (nonLocalizedLabel != null) { proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString()); - proto.write(PackageItemInfoProto.ICON, icon); - proto.write(PackageItemInfoProto.BANNER, banner); } + proto.write(PackageItemInfoProto.ICON, icon); + proto.write(PackageItemInfoProto.BANNER, banner); proto.end(token); } diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java index 79bc9a30b1e2..73addb70ea05 100644 --- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java +++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java @@ -16,13 +16,15 @@ package android.content.pm.permission; +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -32,7 +34,7 @@ import android.permissionpresenterservice.RuntimePermissionPresenterService; import android.util.Log; import com.android.internal.annotations.GuardedBy; -import com.android.internal.os.SomeArgs; +import com.android.internal.util.function.pooled.PooledLambda; import java.util.ArrayList; import java.util.Collections; @@ -109,13 +111,11 @@ public final class RuntimePermissionPresenter { */ public void getAppPermissions(@NonNull String packageName, @NonNull OnResultCallback callback, @Nullable Handler handler) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = packageName; - args.arg2 = callback; - args.arg3 = handler; - Message message = mRemoteService.obtainMessage( - RemoteService.MSG_GET_APP_PERMISSIONS, args); - mRemoteService.processMessage(message); + checkNotNull(packageName); + checkNotNull(callback); + + mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions, + mRemoteService, packageName, callback, handler)); } /** @@ -124,24 +124,20 @@ public final class RuntimePermissionPresenter { * @param packageName The package for which to revoke * @param permissionName The permission to revoke */ - public void revokeRuntimePermission(String packageName, String permissionName) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = packageName; - args.arg2 = permissionName; - - Message message = mRemoteService.obtainMessage( - RemoteService.MSG_REVOKE_APP_PERMISSIONS, args); - mRemoteService.processMessage(message); + public void revokeRuntimePermission(@NonNull String packageName, + @NonNull String permissionName) { + checkNotNull(packageName); + checkNotNull(permissionName); + + mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions, + mRemoteService, packageName, permissionName)); } private static final class RemoteService extends Handler implements ServiceConnection { private static final long UNBIND_TIMEOUT_MILLIS = 10000; - public static final int MSG_GET_APP_PERMISSIONS = 1; - public static final int MSG_GET_APPS_USING_PERMISSIONS = 2; - public static final int MSG_UNBIND = 3; - public static final int MSG_REVOKE_APP_PERMISSIONS = 4; + public static final int MSG_UNBIND = 0; private final Object mLock = new Object(); @@ -191,89 +187,74 @@ public final class RuntimePermissionPresenter { } } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_GET_APP_PERMISSIONS: { - SomeArgs args = (SomeArgs) msg.obj; - final String packageName = (String) args.arg1; - final OnResultCallback callback = (OnResultCallback) args.arg2; - final Handler handler = (Handler) args.arg3; - args.recycle(); - final IRuntimePermissionPresenter remoteInstance; - synchronized (mLock) { - remoteInstance = mRemoteInstance; - } - if (remoteInstance == null) { - return; - } - try { - remoteInstance.getAppPermissions(packageName, - new RemoteCallback(new RemoteCallback.OnResultListener() { - @Override - public void onResult(Bundle result) { - final List<RuntimePermissionPresentationInfo> reportedPermissions; - List<RuntimePermissionPresentationInfo> permissions = null; - if (result != null) { - permissions = result.getParcelableArrayList(KEY_RESULT); - } - if (permissions == null) { - permissions = Collections.emptyList(); - } - reportedPermissions = permissions; - if (handler != null) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onGetAppPermissions(reportedPermissions); - } - }); - } else { - callback.onGetAppPermissions(reportedPermissions); - } + private void getAppPermissions(@NonNull String packageName, + @NonNull OnResultCallback callback, @Nullable Handler handler) { + final IRuntimePermissionPresenter remoteInstance; + synchronized (mLock) { + remoteInstance = mRemoteInstance; + } + if (remoteInstance == null) { + return; + } + try { + remoteInstance.getAppPermissions(packageName, + new RemoteCallback(result -> { + final List<RuntimePermissionPresentationInfo> reportedPermissions; + List<RuntimePermissionPresentationInfo> permissions = null; + if (result != null) { + permissions = result.getParcelableArrayList(KEY_RESULT); + } + if (permissions == null) { + permissions = Collections.emptyList(); + } + reportedPermissions = permissions; + if (handler != null) { + handler.post( + () -> callback.onGetAppPermissions(reportedPermissions)); + } else { + callback.onGetAppPermissions(reportedPermissions); } }, this)); - } catch (RemoteException re) { - Log.e(TAG, "Error getting app permissions", re); - } - scheduleUnbind(); - } break; - - case MSG_UNBIND: { - synchronized (mLock) { - if (mBound) { - mContext.unbindService(this); - mBound = false; - } - mRemoteInstance = null; - } - } break; - - case MSG_REVOKE_APP_PERMISSIONS: { - SomeArgs args = (SomeArgs) msg.obj; - final String packageName = (String) args.arg1; - final String permissionName = (String) args.arg2; - args.recycle(); - final IRuntimePermissionPresenter remoteInstance; - synchronized (mLock) { - remoteInstance = mRemoteInstance; - } - if (remoteInstance == null) { - return; - } - try { - remoteInstance.revokeRuntimePermission(packageName, permissionName); - } catch (RemoteException re) { - Log.e(TAG, "Error getting app permissions", re); - } - } break; + } catch (RemoteException re) { + Log.e(TAG, "Error getting app permissions", re); } + scheduleUnbind(); synchronized (mLock) { scheduleNextMessageIfNeededLocked(); } } + private void revokeAppPermissions(@NonNull String packageName, + @NonNull String permissionName) { + final IRuntimePermissionPresenter remoteInstance; + synchronized (mLock) { + remoteInstance = mRemoteInstance; + } + if (remoteInstance == null) { + return; + } + try { + remoteInstance.revokeRuntimePermission(packageName, permissionName); + } catch (RemoteException re) { + Log.e(TAG, "Error getting app permissions", re); + } + + synchronized (mLock) { + scheduleNextMessageIfNeededLocked(); + } + } + + private void unbind() { + synchronized (mLock) { + if (mBound) { + mContext.unbindService(this); + mBound = false; + } + mRemoteInstance = null; + } + } + @GuardedBy("mLock") private void scheduleNextMessageIfNeededLocked() { if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) { @@ -284,7 +265,8 @@ public final class RuntimePermissionPresenter { private void scheduleUnbind() { removeMessages(MSG_UNBIND); - sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS); + sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this) + .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS); } } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 7148b124253e..5e402c7edc40 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -393,7 +393,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * doesn't have any recommendation for this use case or the recommended configurations * are invalid. */ - public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap( + public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap( @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) { if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) && (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) || diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index dc6cffc4ebee..448591f2c52a 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -968,6 +968,36 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void close(); /** + * Checks whether a particular {@link SessionConfiguration} is supported by the camera device. + * + * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result + * confirms whether or not the passed session configuration can be successfully used to + * create a camera capture session using + * {@link CameraDevice#createCaptureSession(SessionConfiguration)}. + * </p> + * + * <p>The method can be called at any point before, during and after active capture session. + * It must not impact normal camera behavior in any way and must complete significantly + * faster than creating a regular or constrained capture session.</p> + * + * <p>Note that session parameters will be ignored and calls to + * {@link SessionConfiguration#setSessionParameters} are not required.</p> + * + * @return {@code true} if the given session configuration is supported by the camera device + * {@code false} otherwise. + * @throws UnsupportedOperationException if the query operation is not supported by the camera + * device + * @throws IllegalArgumentException if the session configuration is invalid + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + */ + public boolean isSessionConfigurationSupported( + @NonNull SessionConfiguration sessionConfig) throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** * A callback objects for receiving updates about the state of a camera device. * * <p>A callback instance must be provided to the {@link CameraManager#openCamera} method to diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 7810e6c19abe..57b608f0fd84 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -703,6 +703,17 @@ public class CameraDeviceImpl extends CameraDevice } } + @Override + public boolean isSessionConfigurationSupported( + @NonNull SessionConfiguration sessionConfig) throws CameraAccessException, + UnsupportedOperationException, IllegalArgumentException { + synchronized(mInterfaceLock) { + checkIfCameraClosedOrInError(); + + return mRemoteDevice.isSessionConfigurationSupported(sessionConfig); + } + } + /** * For use by backwards-compatibility code only. */ diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index 1f4ed13ee09e..c8ded8dde54a 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -30,9 +30,11 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.utils.SubmitInfo; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.view.Surface; /** @@ -181,6 +183,25 @@ public class ICameraDeviceUserWrapper { } } + public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) + throws CameraAccessException { + try { + return mRemoteDevice.isSessionConfigurationSupported(sessionConfig); + } catch (ServiceSpecificException e) { + if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { + throw new UnsupportedOperationException("Session configuration query not " + + "supported"); + } else if (e.errorCode == ICameraService.ERROR_ILLEGAL_ARGUMENT) { + throw new IllegalArgumentException("Invalid session configuration"); + } + + throw e; + } catch (Throwable t) { + CameraManager.throwAsPublicException(t); + throw new UnsupportedOperationException("Unexpected exception", t); + } + } + public long flush() throws CameraAccessException { try { return mRemoteDevice.flush(); diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index bc7b1260751e..123eb8ee1431 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -28,6 +28,7 @@ import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; import android.hardware.camera2.impl.PhysicalCaptureResultInfo; import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.utils.SubmitInfo; import android.os.ConditionVariable; import android.os.IBinder; @@ -480,6 +481,12 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override + public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) { + // TODO: Add support for this in legacy mode + throw new UnsupportedOperationException("Session configuration query not supported!"); + } + + @Override public void beginConfigure() { if (DEBUG) { Log.d(TAG, "beginConfigure called."); diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index 83a02285e720..1b28d614a7f2 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -521,9 +521,10 @@ public class SurfaceTextureRenderer { clearState(); } - private void makeCurrent(EGLSurface surface) { + private void makeCurrent(EGLSurface surface) + throws LegacyExceptionUtils.BufferQueueAbandonedException { EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext); - checkEglError("makeCurrent"); + checkEglDrawError("makeCurrent"); } private boolean swapBuffers(EGLSurface surface) @@ -557,6 +558,17 @@ public class SurfaceTextureRenderer { } } + private void checkEglDrawError(String msg) + throws LegacyExceptionUtils.BufferQueueAbandonedException { + int error; + if ((error = EGL14.eglGetError()) == EGL14.EGL_BAD_NATIVE_WINDOW) { + throw new LegacyExceptionUtils.BufferQueueAbandonedException(); + } + if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { + throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error)); + } + } + private void checkEglError(String msg) { int error; if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { @@ -709,8 +721,14 @@ public class SurfaceTextureRenderer { if (mConversionSurfaces.size() > 0) { configureEGLPbufferSurfaces(mConversionSurfaces); } - makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface : + + try { + makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface : mConversionSurfaces.get(0).eglSurface); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping configuration... ", e); + } + initializeGLState(); mSurfaceTexture = new SurfaceTexture(getTextureId()); @@ -798,9 +816,9 @@ public class SurfaceTextureRenderer { } for (EGLSurfaceHolder holder : mConversionSurfaces) { if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { - makeCurrent(holder.eglSurface); // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip try { + makeCurrent(holder.eglSurface); drawFrame(mSurfaceTexture, holder.width, holder.height, (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ? FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL); diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java index 59e4a3366995..068c0ce8d052 100644 --- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java @@ -17,6 +17,7 @@ package android.hardware.camera2.params; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.ImageFormat; @@ -173,7 +174,7 @@ public final class RecommendedStreamConfigurationMap { * * @return Use case id. */ - public int getRecommendedUseCase() { + public @RecommendedUsecase int getRecommendedUseCase() { return mUsecase; } @@ -326,7 +327,7 @@ public final class RecommendedStreamConfigurationMap { * @throws IllegalArgumentException if input size does not exist in the return value of * getHighSpeedVideoSizes */ - public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) { + public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(@NonNull Size size) { return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size)); } @@ -349,14 +350,14 @@ public final class RecommendedStreamConfigurationMap { * For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}. * </p> * - * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()} + * @param fpsRange one of the FPS ranges returned by {@link #getHighSpeedVideoFpsRanges()} * @return A non-modifiable set of video sizes to create high speed capture sessions for high * speed streaming use cases. * * @throws IllegalArgumentException if input FPS range does not exist in the return value of * getHighSpeedVideoFpsRanges */ - public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) { + public @Nullable Set<Size> getHighSpeedVideoSizesFor(@NonNull Range<Integer> fpsRange) { return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange)); } @@ -390,10 +391,8 @@ public final class RecommendedStreamConfigurationMap { * 0 if the minimum frame duration is not available. * * @throws IllegalArgumentException if {@code format} or {@code size} was not supported - * @throws NullPointerException if {@code size} was {@code null} - * */ - public long getOutputMinFrameDuration(int format, Size size) { + public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) { return mRecommendedMap.getOutputMinFrameDuration(format, size); } @@ -409,9 +408,8 @@ public final class RecommendedStreamConfigurationMap { * @return a stall duration {@code >=} 0 in nanoseconds * * @throws IllegalArgumentException if {@code format} or {@code size} was not supported - * @throws NullPointerException if {@code size} was {@code null} */ - public long getOutputStallDuration(int format, Size size) { + public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) { return mRecommendedMap.getOutputStallDuration(format, size); } @@ -422,16 +420,12 @@ public final class RecommendedStreamConfigurationMap { * </p> * * @param klass - * a non-{@code null} {@link Class} object reference + * a {@link Class} object reference * @return * a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format, * or {@code null} if the {@code klass} is not a supported output. - * - * - * @throws NullPointerException if {@code klass} was {@code null} - * */ - public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) { + public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) { if (mSupportsPrivate) { return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass)); } @@ -447,16 +441,15 @@ public final class RecommendedStreamConfigurationMap { * {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p> * * @param klass - * a class which has a non-empty array returned by {@link #getOutputSizes(Class)} + * a class which has a non-empty array returned by {@link #getOutputSizes(Class)} * @param size an output-compatible size * @return a minimum frame duration {@code >} 0 in nanoseconds, or * 0 if the minimum frame duration is not available. * * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported - * @throws NullPointerException if {@code size} or {@code klass} was {@code null} - * */ - public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) { + public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass, + @NonNull final Size size) { if (mSupportsPrivate) { return mRecommendedMap.getOutputMinFrameDuration(klass, size); } @@ -473,14 +466,13 @@ public final class RecommendedStreamConfigurationMap { * @param klass * a class which has a non-empty array returned by {@link #getOutputSizes(Class)}. * @param size an output-compatible size - * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is + * @return a minimum frame duration {@code >} 0 in nanoseconds, or 0 if the stall duration is * not available. * * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported - * @throws NullPointerException if {@code size} or {@code klass} was {@code null} - * */ - public <T> long getOutputStallDuration(final Class<T> klass, final Size size) { + public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass, + @NonNull final Size size) { if (mSupportsPrivate) { return mRecommendedMap.getOutputStallDuration(klass, size); } @@ -495,14 +487,13 @@ public final class RecommendedStreamConfigurationMap { * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}. * </p> * - * @param surface a non-{@code null} {@link Surface} object reference + * @param surface a {@link Surface} object reference * @return {@code true} if this is supported, {@code false} otherwise * - * @throws NullPointerException if {@code surface} was {@code null} * @throws IllegalArgumentException if the Surface endpoint is no longer valid * */ - public boolean isOutputSupportedFor(Surface surface) { + public boolean isOutputSupportedFor(@NonNull Surface surface) { return mRecommendedMap.isOutputSupportedFor(surface); } diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 8a8afb24b3f8..3ea58ad83327 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -27,6 +27,10 @@ import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.utils.HashCodeHelpers; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; import java.util.Collections; import java.util.List; @@ -40,7 +44,9 @@ import static com.android.internal.util.Preconditions.*; /** * A helper class that aggregates all supported arguments for capture session initialization. */ -public final class SessionConfiguration { +public final class SessionConfiguration implements Parcelable { + private static final String TAG = "SessionConfiguration"; + /** * A regular session type containing instances of {@link OutputConfiguration} running * at regular non high speed FPS ranges and optionally {@link InputConfiguration} for @@ -110,6 +116,108 @@ public final class SessionConfiguration { } /** + * Create a SessionConfiguration from Parcel. + * No support for parcelable 'mStateCallback', 'mExecutor' and 'mSessionParameters' yet. + */ + private SessionConfiguration(@NonNull Parcel source) { + int sessionType = source.readInt(); + int inputWidth = source.readInt(); + int inputHeight = source.readInt(); + int inputFormat = source.readInt(); + ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>(); + source.readTypedList(outConfigs, OutputConfiguration.CREATOR); + + if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) { + mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat); + } + mSessionType = sessionType; + mOutputConfigurations = outConfigs; + } + + public static final Parcelable.Creator<SessionConfiguration> CREATOR = + new Parcelable.Creator<SessionConfiguration> () { + @Override + public SessionConfiguration createFromParcel(Parcel source) { + try { + SessionConfiguration sessionConfiguration = new SessionConfiguration(source); + return sessionConfiguration; + } catch (Exception e) { + Log.e(TAG, "Exception creating SessionConfiguration from parcel", e); + return null; + } + } + + @Override + public SessionConfiguration[] newArray(int size) { + return new SessionConfiguration[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (dest == null) { + throw new IllegalArgumentException("dest must not be null"); + } + dest.writeInt(mSessionType); + if (mInputConfig != null) { + dest.writeInt(mInputConfig.getWidth()); + dest.writeInt(mInputConfig.getHeight()); + dest.writeInt(mInputConfig.getFormat()); + } else { + dest.writeInt(/*inputWidth*/ 0); + dest.writeInt(/*inputHeight*/ 0); + dest.writeInt(/*inputFormat*/ -1); + } + dest.writeTypedList(mOutputConfigurations); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Check if this {@link SessionConfiguration} is equal to another {@link SessionConfiguration}. + * + * <p>Two output session configurations are only equal if and only if the underlying input + * configuration, output configurations, and session type are equal. </p> + * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (this == obj) { + return true; + } else if (obj instanceof SessionConfiguration) { + final SessionConfiguration other = (SessionConfiguration) obj; + if (mInputConfig != other.mInputConfig || mSessionType != other.mSessionType || + mOutputConfigurations.size() != other.mOutputConfigurations.size()) { + return false; + } + + for (int i = 0; i < mOutputConfigurations.size(); i++) { + if (!mOutputConfigurations.get(i).equals(other.mOutputConfigurations.get(i))) + return false; + } + + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(), + mSessionType); + } + + /** * Retrieve the type of the capture session. * * @return The capture session type. diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java index 0a76c2bc724e..a4c1332b45d1 100644 --- a/core/java/android/hardware/display/ColorDisplayManager.java +++ b/core/java/android/hardware/display/ColorDisplayManager.java @@ -16,20 +16,81 @@ package android.hardware.display; +import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; import com.android.internal.R; /** * Manages the display's color transforms and modes. + * * @hide */ +@SystemService(Context.COLOR_DISPLAY_SERVICE) public final class ColorDisplayManager { + private final ColorDisplayManagerInternal mManager; + + /** + * @hide + */ + public ColorDisplayManager() { + mManager = ColorDisplayManagerInternal.getInstance(); + } + + /** + * Returns whether the device has a wide color gamut display. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) + public boolean isDeviceColorManaged() { + return mManager.isDeviceColorManaged(); + } + /** * Returns {@code true} if Night Display is supported by the device. */ public static boolean isNightDisplayAvailable(Context context) { return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable); } + + private static class ColorDisplayManagerInternal { + + private static ColorDisplayManagerInternal sInstance; + + private final IColorDisplayManager mCdm; + + private ColorDisplayManagerInternal(IColorDisplayManager colorDisplayManager) { + mCdm = colorDisplayManager; + } + + public static ColorDisplayManagerInternal getInstance() { + synchronized (ColorDisplayManagerInternal.class) { + if (sInstance == null) { + try { + IBinder b = ServiceManager.getServiceOrThrow(Context.COLOR_DISPLAY_SERVICE); + sInstance = new ColorDisplayManagerInternal( + IColorDisplayManager.Stub.asInterface(b)); + } catch (ServiceNotFoundException e) { + throw new IllegalStateException(e); + } + } + return sInstance; + } + } + + boolean isDeviceColorManaged() { + try { + return mCdm.isDeviceColorManaged(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } } diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl new file mode 100644 index 000000000000..f7865899812a --- /dev/null +++ b/core/java/android/hardware/display/IColorDisplayManager.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +/** @hide */ +interface IColorDisplayManager { + boolean isDeviceColorManaged(); +}
\ No newline at end of file diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 64314a7d8060..2ae796c1ec56 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -85,6 +85,15 @@ public class Binder implements IBinder { public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE /** + * Value to represents that a calling work source is not set. + * + * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource. + * + * @hide + */ + public static final int UNSET_WORKSOURCE = -1; + + /** * Control whether dump() calls are allowed. */ private static volatile String sDumpDisabled = null; @@ -449,8 +458,6 @@ public class Binder implements IBinder { * } * </pre> * - * <p>The work source will be propagated for future outgoing binder transactions - * executed on this thread. * @hide **/ @CriticalNative @@ -912,6 +919,16 @@ public class Binder implements IBinder { // Entry point from android_util_Binder.cpp's onTransact private boolean execTransact(int code, long dataObj, long replyObj, int flags) { + final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid()); + try { + return execTransactInternal(code, dataObj, replyObj, flags); + } finally { + ThreadLocalWorkSource.restore(origWorkSource); + } + } + + private boolean execTransactInternal(int code, long dataObj, long replyObj, + int flags) { // Make sure the observer won't change while processing a transaction. final BinderInternal.Observer observer = sObserver; final CallSession callSession = @@ -925,7 +942,6 @@ public class Binder implements IBinder { // Log any exceptions as warnings, don't silently suppress them. // If the call was FLAG_ONEWAY then these exceptions disappear into the ether. final boolean tracingEnabled = Binder.isTracingEnabled(); - final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid()); try { if (tracingEnabled) { final String transactionName = getTransactionName(code); @@ -952,7 +968,6 @@ public class Binder implements IBinder { } res = true; } finally { - ThreadLocalWorkSource.restore(origWorkSource); if (tracingEnabled) { Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); } @@ -972,7 +987,6 @@ public class Binder implements IBinder { if (observer != null) { observer.callEnded(callSession, requestSizeBytes, replySizeBytes); } - return res; } } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 900b62d98bf4..8cafbde8e3eb 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -269,7 +269,7 @@ public class GraphicsEnvironment { } // If no temp rules, load the real ones from the APK - if (rulesFd == null) { + if (DEBUG && (rulesFd == null)) { // Pass the rules file to loader for ANGLE decisions AssetManager angleAssets = null; diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 894015ff5a92..c3e04894c6c9 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -17,7 +17,9 @@ package android.os; import android.Manifest.permission; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; @@ -25,11 +27,15 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.service.dreams.Sandman; +import android.util.ArrayMap; import android.util.Log; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.Preconditions; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * This class gives you control of the power state of the device. @@ -643,6 +649,9 @@ public final class PowerManager { final IPowerManager mService; final Handler mHandler; + IThermalService mThermalService; + private ArrayMap<ThermalStatusCallback, IThermalStatusListener> mCallbackMap = new ArrayMap<>(); + IDeviceIdleController mIDeviceIdleController; /** @@ -1443,6 +1452,159 @@ public final class PowerManager { } /** + * Thermal status code: Not under throttling. + */ + public static final int THERMAL_STATUS_NONE = Temperature.THROTTLING_NONE; + + /** + * Thermal status code: Light throttling where UX is not impacted. + */ + public static final int THERMAL_STATUS_LIGHT = Temperature.THROTTLING_LIGHT; + + /** + * Thermal status code: Moderate throttling where UX is not largely impacted. + */ + public static final int THERMAL_STATUS_MODERATE = Temperature.THROTTLING_MODERATE; + + /** + * Thermal status code: Severe throttling where UX is largely impacted. + */ + public static final int THERMAL_STATUS_SEVERE = Temperature.THROTTLING_SEVERE; + + /** + * Thermal status code: Platform has done everything to reduce power. + */ + public static final int THERMAL_STATUS_CRITICAL = Temperature.THROTTLING_CRITICAL; + + /** + * Thermal status code: Key components in platform are shutting down due to thermal condition. + * Device functionalities will be limited. + */ + public static final int THERMAL_STATUS_EMERGENCY = Temperature.THROTTLING_EMERGENCY; + + /** + * Thermal status code: Need shutdown immediately. + */ + public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN; + + /** @hide */ + @IntDef(prefix = { "THERMAL_STATUS_" }, value = { + THERMAL_STATUS_NONE, + THERMAL_STATUS_LIGHT, + THERMAL_STATUS_MODERATE, + THERMAL_STATUS_SEVERE, + THERMAL_STATUS_CRITICAL, + THERMAL_STATUS_EMERGENCY, + THERMAL_STATUS_SHUTDOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ThermalStatus {} + + /** + * This function returns the current thermal status of the device. + * + * @return thermal status as int, {@link #THERMAL_STATUS_NONE} if device in not under + * thermal throttling. + */ + public @ThermalStatus int getCurrentThermalStatus() { + synchronized (this) { + if (mThermalService == null) { + mThermalService = IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + try { + return mThermalService.getCurrentThermalStatus(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + } + + /** + * Callback passed to + * {@link PowerManager#registerThermalStatusCallback} and + * {@link PowerManager#unregisterThermalStatusCallback} + * to notify caller of thermal status. + */ + public abstract static class ThermalStatusCallback { + + /** + * Called when overall thermal throttling status changed. + * @param status defined in {@link android.os.Temperature}. + */ + public void onStatusChange(@ThermalStatus int status) {} + } + + /** + * This function registers a callback for thermal status change. + * + * @param callback callback to be registered. + * @param executor {@link Executor} to handle the callbacks. + */ + public void registerThermalStatusCallback( + @NonNull ThermalStatusCallback callback, @NonNull @CallbackExecutor Executor executor) { + Preconditions.checkNotNull(callback, "callback cannnot be null"); + Preconditions.checkNotNull(executor, "executor cannnot be null"); + synchronized (this) { + if (mThermalService == null) { + mThermalService = IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + try { + if (mCallbackMap.containsKey(callback)) { + throw new IllegalArgumentException("ThermalStatusCallback already registered"); + } + IThermalStatusListener listener = new IThermalStatusListener.Stub() { + @Override + public void onStatusChange(int status) { + executor.execute(() -> { + callback.onStatusChange(status); + }); + } + }; + if (mThermalService.registerThermalStatusListener(listener)) { + mCallbackMap.put(callback, listener); + } else { + throw new RuntimeException("ThermalStatusCallback failed to register"); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * This function unregisters a callback for thermal status change. + * + * @param callback to be unregistered. + * + * see {@link #registerThermalStatusCallback} + */ + public void unregisterThermalStatusCallback(ThermalStatusCallback callback) { + Preconditions.checkNotNull(callback, "callback cannnot be null"); + synchronized (this) { + if (mThermalService == null) { + mThermalService = IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + try { + IThermalStatusListener listener = mCallbackMap.get(callback); + if (listener == null) { + throw new IllegalArgumentException("ThermalStatusCallback not registered"); + } + if (mThermalService.unregisterThermalStatusListener(listener)) { + mCallbackMap.remove(callback); + } else { + throw new RuntimeException("ThermalStatusCallback failed to unregister"); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * If true, the doze component is not started until after the screen has been * turned off and the screen off animation has been performed. * @hide diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java index 21aba595bd5e..5499181c3cdb 100644 --- a/core/java/android/os/Temperature.java +++ b/core/java/android/os/Temperature.java @@ -44,7 +44,7 @@ public final class Temperature implements Parcelable { THROTTLING_MODERATE, THROTTLING_SEVERE, THROTTLING_CRITICAL, - THROTTLING_WARNING, + THROTTLING_EMERGENCY, THROTTLING_SHUTDOWN, }) @Retention(RetentionPolicy.SOURCE) @@ -56,7 +56,7 @@ public final class Temperature implements Parcelable { public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE; public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE; public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL; - public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING; + public static final int THROTTLING_EMERGENCY = ThrottlingSeverity.EMERGENCY; public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN; @IntDef(prefix = { "TYPE_" }, value = { diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java index 18aea03315ef..a41a64422038 100644 --- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java +++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java @@ -16,6 +16,9 @@ package android.permissionpresenterservice; +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; @@ -27,12 +30,8 @@ import android.content.pm.permission.RuntimePermissionPresenter; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.RemoteCallback; -import com.android.internal.os.SomeArgs; - import java.util.List; /** @@ -63,7 +62,7 @@ public abstract class RuntimePermissionPresenterService extends Service { @Override public final void attachBaseContext(Context base) { super.attachBaseContext(base); - mHandler = new MyHandler(base.getMainLooper()); + mHandler = new Handler(base.getMainLooper()); } /** @@ -71,7 +70,8 @@ public abstract class RuntimePermissionPresenterService extends Service { * * @param packageName The package for which to query. */ - public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName); + public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions( + @NonNull String packageName); /** * Revokes the permission {@code permissionName} for app {@code packageName} @@ -87,61 +87,35 @@ public abstract class RuntimePermissionPresenterService extends Service { return new IRuntimePermissionPresenter.Stub() { @Override public void getAppPermissions(String packageName, RemoteCallback callback) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = packageName; - args.arg2 = callback; - mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS, - args).sendToTarget(); + checkNotNull(packageName, "packageName"); + checkNotNull(callback, "callback"); + + mHandler.sendMessage( + obtainMessage(RuntimePermissionPresenterService::getAppPermissions, + RuntimePermissionPresenterService.this, packageName, callback)); } @Override public void revokeRuntimePermission(String packageName, String permissionName) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = packageName; - args.arg2 = permissionName; - mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION, - args).sendToTarget(); + checkNotNull(packageName, "packageName"); + checkNotNull(permissionName, "permissionName"); + + mHandler.sendMessage( + obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission, + RuntimePermissionPresenterService.this, packageName, + permissionName)); } }; } - private final class MyHandler extends Handler { - public static final int MSG_GET_APP_PERMISSIONS = 1; - public static final int MSG_GET_APPS_USING_PERMISSIONS = 2; - public static final int MSG_REVOKE_APP_PERMISSION = 3; - - public MyHandler(Looper looper) { - super(looper, null, false); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_GET_APP_PERMISSIONS: { - SomeArgs args = (SomeArgs) msg.obj; - String packageName = (String) args.arg1; - RemoteCallback callback = (RemoteCallback) args.arg2; - args.recycle(); - List<RuntimePermissionPresentationInfo> permissions = - onGetAppPermissions(packageName); - if (permissions != null && !permissions.isEmpty()) { - Bundle result = new Bundle(); - result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, - permissions); - callback.sendResult(result); - } else { - callback.sendResult(null); - } - } break; - case MSG_REVOKE_APP_PERMISSION: { - SomeArgs args = (SomeArgs) msg.obj; - String packageName = (String) args.arg1; - String permissionName = (String) args.arg2; - args.recycle(); - - onRevokeRuntimePermission(packageName, permissionName); - } break; - } + private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { + List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); + if (permissions != null && !permissions.isEmpty()) { + Bundle result = new Bundle(); + result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions); + callback.sendResult(result); + } else { + callback.sendResult(null); } } } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index e032c1896a4b..37c84bd74395 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -152,6 +152,17 @@ public final class DocumentsContract { "android:query-arg-last-modified-after"; /** + * Key for {@link DocumentsProvider} to decide whether the files that + * have been added to MediaStore should be excluded. If the value is + * true, exclude them. Otherwise, include them. + * + * @see DocumentsProvider#querySearchDocuments(String, String[], + * Bundle) + * {@hide} + */ + public static final String QUERY_ARG_EXCLUDE_MEDIA = "android:query-arg-exclude-media"; + + /** * Sets the desired initial location visible to user when file chooser is shown. * * <p>Applicable to {@link Intent} with actions: @@ -1017,6 +1028,7 @@ public final class DocumentsContract { /** * Get the handled query arguments from the query bundle. The handled arguments are + * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA}, * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME}, * {@link DocumentsContract#QUERY_ARG_MIME_TYPES}, * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and @@ -1032,6 +1044,11 @@ public final class DocumentsContract { } final ArrayList<String> args = new ArrayList<>(); + + if (queryArgs.keySet().contains(QUERY_ARG_EXCLUDE_MEDIA)) { + args.add(QUERY_ARG_EXCLUDE_MEDIA); + } + if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) { args.add(QUERY_ARG_DISPLAY_NAME); } diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 58f82134ec50..6ab72c7bb372 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -677,6 +677,7 @@ public abstract class DocumentsProvider extends ContentProvider { * cursor. If {@code null} all supported columns should be * included. * @param queryArgs the query arguments. + * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA}, * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME}, * {@link DocumentsContract#QUERY_ARG_MIME_TYPES}, * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER}, diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 9e26a368ad80..0299e416707a 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -37,6 +37,7 @@ import android.database.DatabaseUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; +import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; @@ -1166,13 +1167,25 @@ public final class MediaStore { /** * The latitude where the image was captured. * <P>Type: DOUBLE</P> + * + * @deprecated location details are no longer indexed for privacy + * reasons, and this value is now always {@code null}. + * You can still manually obtain location metadata using + * {@link ExifInterface#getLatLong(float[])}. */ + @Deprecated public static final String LATITUDE = "latitude"; /** * The longitude where the image was captured. * <P>Type: DOUBLE</P> + * + * @deprecated location details are no longer indexed for privacy + * reasons, and this value is now always {@code null}. + * You can still manually obtain location metadata using + * {@link ExifInterface#getLatLong(float[])}. */ + @Deprecated public static final String LONGITUDE = "longitude"; /** @@ -2410,13 +2423,25 @@ public final class MediaStore { /** * The latitude where the video was captured. * <P>Type: DOUBLE</P> + * + * @deprecated location details are no longer indexed for privacy + * reasons, and this value is now always {@code null}. + * You can still manually obtain location metadata using + * {@link ExifInterface#getLatLong(float[])}. */ + @Deprecated public static final String LATITUDE = "latitude"; /** * The longitude where the video was captured. * <P>Type: DOUBLE</P> + * + * @deprecated location details are no longer indexed for privacy + * reasons, and this value is now always {@code null}. + * You can still manually obtain location metadata using + * {@link ExifInterface#getLatLong(float[])}. */ + @Deprecated public static final String LONGITUDE = "longitude"; /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b53d9af5d4e4..c3217a2189cf 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4252,6 +4252,7 @@ public final class Settings { PUBLIC_SETTINGS.add(BLUETOOTH_DISCOVERABILITY_TIMEOUT); PUBLIC_SETTINGS.add(NEXT_ALARM_FORMATTED); PUBLIC_SETTINGS.add(FONT_SCALE); + PUBLIC_SETTINGS.add(SYSTEM_LOCALES); PUBLIC_SETTINGS.add(DIM_SCREEN); PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT); PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS); @@ -12838,6 +12839,23 @@ public final class Settings { "max_sound_trigger_detection_service_ops_per_day"; /** + * Property used by {@code com.android.server.SystemServer} on start to decide whether + * the Smart Suggestions service should be created or not + * + * <p>By default it should *NOT* be set (in which case the decision is based on whether + * the OEM provides an implementation for the service), but it can be overridden to: + * + * <ul> + * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency. + * <li>Enable the CTS tests to be run on AOSP builds + * </ul> + * + * @hide + */ + public static final String SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED = + "smart_suggestions_service_explicitly_enabled"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -13510,6 +13528,13 @@ public final class Settings { */ public static final String WARNING_TEMPERATURE = "warning_temperature"; + + /** + * USB Temperature at which the high temperature alarm notification should be shown. + * @hide + */ + public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature"; + /** * Whether the diskstats logging task is enabled/disabled. * @hide diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java index af2da79170ef..ddf37f737296 100644 --- a/core/java/android/service/intelligence/FillCallback.java +++ b/core/java/android/service/intelligence/FillCallback.java @@ -15,8 +15,10 @@ */ package android.service.intelligence; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.service.intelligence.SmartSuggestionsService.AutofillProxy; /** * Callback used to indicate at {@link FillRequest} has been fulfilled. @@ -25,8 +27,11 @@ import android.annotation.SystemApi; */ @SystemApi public final class FillCallback { + private final AutofillProxy mProxy; - FillCallback() {} + FillCallback(@NonNull AutofillProxy proxy) { + mProxy = proxy; + } /** * Sets the response associated with the request. @@ -35,6 +40,7 @@ public final class FillCallback { * could not provide autofill for the request. */ public void onSuccess(@Nullable FillResponse response) { + mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS); final FillWindow fillWindow = response.getFillWindow(); if (fillWindow != null) { fillWindow.show(); diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java index f68db9df6fe3..53e99a5fd3b8 100644 --- a/core/java/android/service/intelligence/FillRequest.java +++ b/core/java/android/service/intelligence/FillRequest.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.service.intelligence.SmartSuggestionsService.AutofillProxy; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; /** * Represents a request to augment-fill an activity. @@ -52,6 +53,14 @@ public final class FillRequest { } /** + * Gets the current value of the field that triggered the request. + */ + @NonNull + public AutofillValue getFocusedAutofillValue() { + return mProxy.focusedValue; + } + + /** * Gets the Smart Suggestions object used to embed the autofill UI. * * @return object used to embed the autofill UI, or {@code null} if not supported. diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java index 309f6a1b6f1b..39d7e08a68d9 100644 --- a/core/java/android/service/intelligence/FillWindow.java +++ b/core/java/android/service/intelligence/FillWindow.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi; import android.app.Dialog; import android.graphics.Rect; import android.service.intelligence.PresentationParams.Area; +import android.service.intelligence.SmartSuggestionsService.AutofillProxy; import android.util.Log; import android.view.Gravity; import android.view.View; @@ -33,6 +34,8 @@ import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; +import dalvik.system.CloseGuard; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -73,6 +76,7 @@ public final class FillWindow { @interface Flags{} private final Object mLock = new Object(); + private final CloseGuard mCloseGuard = CloseGuard.get(); @GuardedBy("mLock") private Dialog mDialog; @@ -80,6 +84,8 @@ public final class FillWindow { @GuardedBy("mLock") private boolean mDestroyed; + private AutofillProxy mProxy; + /** * Updates the content of the window. * @@ -123,6 +129,8 @@ public final class FillWindow { synchronized (mLock) { checkNotDestroyedLocked(); + mProxy = area.proxy; + // TODO(b/111330312): once we have the SurfaceControl approach, we should update the // window instead of destroying. In fact, it might be better to allocate a full window // initially, which is transparent (and let touches get through) everywhere but in the @@ -133,6 +141,7 @@ public final class FillWindow { // etc. mDialog = new Dialog(rootView.getContext()); + mCloseGuard.open("destroy"); final Window window = mDialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); @@ -156,7 +165,7 @@ public final class FillWindow { Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView); } - area.proxy.setFillWindow(this); + mProxy.setFillWindow(this); return true; } } @@ -173,6 +182,9 @@ public final class FillWindow { } mDialog.show(); + if (mProxy != null) { + mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN); + } } } @@ -182,15 +194,29 @@ public final class FillWindow { * <p>Once destroyed, this window cannot be used anymore */ public void destroy() { - if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed); + if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog); synchronized (this) { - if (mDestroyed) return; + if (mDestroyed || mDialog == null) return; - if (mDialog != null) { - mDialog.dismiss(); - mDialog = null; + mDialog.dismiss(); + mDialog = null; + if (mProxy != null) { + mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); } + mCloseGuard.close(); + } + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + destroy(); + } finally { + super.finalize(); } } diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl index d6b31079e34c..2b924fbaadc4 100644 --- a/core/java/android/service/intelligence/IIntelligenceService.aidl +++ b/core/java/android/service/intelligence/IIntelligenceService.aidl @@ -23,6 +23,7 @@ import android.service.intelligence.InteractionContext; import android.service.intelligence.SnapshotData; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.intelligence.ContentCaptureEvent; import java.util.List; @@ -45,7 +46,8 @@ oneway interface IIntelligenceService { in SnapshotData snapshotData); void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient, - int autofilSessionId, in AutofillId focusedId); + int autofilSessionId, in AutofillId focusedId, + in AutofillValue focusedValue, long requestTime); void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId); } diff --git a/core/java/android/service/intelligence/SmartSuggestionsService.java b/core/java/android/service/intelligence/SmartSuggestionsService.java index 0e29e70ecc3b..b684b0208d8d 100644 --- a/core/java/android/service/intelligence/SmartSuggestionsService.java +++ b/core/java/android/service/intelligence/SmartSuggestionsService.java @@ -18,6 +18,7 @@ package android.service.intelligence; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -30,10 +31,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.SystemClock; import android.service.intelligence.PresentationParams.SystemPopupPresentationParams; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; +import android.util.Slog; +import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAugmentedAutofillManagerClient; @@ -43,6 +47,8 @@ import com.android.internal.annotations.GuardedBy; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -110,9 +116,11 @@ public abstract class SmartSuggestionsService extends Service { @Override public void onAutofillRequest(InteractionSessionId sessionId, IBinder client, - int autofilSessionId, AutofillId focusedId) { + int autofilSessionId, AutofillId focusedId, AutofillValue focusedValue, + long requestTime) { mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest, - SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId)); + SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId, + focusedValue, requestTime)); } @Override @@ -229,13 +237,15 @@ public abstract class SmartSuggestionsService extends Service { @NonNull ContentCaptureEventsRequest request); private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId, - @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) { + @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue, long requestTime) { if (mAutofillProxies == null) { mAutofillProxies = new ArrayMap<>(); } AutofillProxy proxy = mAutofillProxies.get(sessionId); if (proxy == null) { - proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId); + proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId, focusedValue, + requestTime); mAutofillProxies.put(sessionId, proxy); } else { // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging @@ -244,7 +254,7 @@ public abstract class SmartSuggestionsService extends Service { // TODO(b/111330312): set cancellation signal final CancellationSignal cancellationSignal = null; onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal, - new FillController(proxy), new FillCallback()); + new FillController(proxy), new FillCallback(proxy)); } /** @@ -332,11 +342,32 @@ public abstract class SmartSuggestionsService extends Service { /** @hide */ static final class AutofillProxy { + + static final int REPORT_EVENT_ON_SUCCESS = 1; + static final int REPORT_EVENT_UI_SHOWN = 2; + static final int REPORT_EVENT_UI_DESTROYED = 3; + + @IntDef(prefix = { "REPORT_EVENT_" }, value = { + REPORT_EVENT_ON_SUCCESS, + REPORT_EVENT_UI_SHOWN, + REPORT_EVENT_UI_DESTROYED + }) + @Retention(RetentionPolicy.SOURCE) + @interface ReportEvent{} + + private final Object mLock = new Object(); private final IAugmentedAutofillManagerClient mClient; private final int mAutofillSessionId; public final InteractionSessionId sessionId; public final AutofillId focusedId; + public final AutofillValue focusedValue; + + // Objects used to log metrics + private final long mRequestTime; + private long mOnSuccessTime; + private long mUiFirstShownTime; + private long mUiFirstDestroyedTime; @GuardedBy("mLock") private SystemPopupPresentationParams mSmartSuggestion; @@ -345,11 +376,14 @@ public abstract class SmartSuggestionsService extends Service { private FillWindow mFillWindow; private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client, - int autofillSessionId, @NonNull AutofillId focusedId) { + int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue, long requestTime) { this.sessionId = sessionId; mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client); mAutofillSessionId = autofillSessionId; this.focusedId = focusedId; + this.focusedValue = focusedValue; + this.mRequestTime = requestTime; // TODO(b/111330312): linkToDeath } @@ -400,9 +434,50 @@ public abstract class SmartSuggestionsService extends Service { } } + // Used for metrics. + public void report(@ReportEvent int event) { + switch (event) { + case REPORT_EVENT_ON_SUCCESS: + if (mOnSuccessTime == 0) { + mOnSuccessTime = SystemClock.elapsedRealtime(); + if (DEBUG) { + Slog.d(TAG, "Service responsed in " + + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime)); + } + } + break; + case REPORT_EVENT_UI_SHOWN: + if (mUiFirstShownTime == 0) { + mUiFirstShownTime = SystemClock.elapsedRealtime(); + if (DEBUG) { + Slog.d(TAG, "UI shown in " + + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime)); + } + } + break; + case REPORT_EVENT_UI_DESTROYED: + if (mUiFirstDestroyedTime == 0) { + mUiFirstDestroyedTime = SystemClock.elapsedRealtime(); + if (DEBUG) { + Slog.d(TAG, "UI destroyed in " + + TimeUtils.formatDuration( + mUiFirstDestroyedTime - mRequestTime)); + } + } + break; + default: + Slog.w(TAG, "invalid event reported: " + event); + } + // TODO(b/111330312): log metrics as well + } + + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId); pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId); + if (focusedValue != null) { + pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue); + } pw.print(prefix); pw.print("client: "); pw.println(mClient); final String prefix2 = prefix + " "; if (mFillWindow != null) { @@ -413,6 +488,23 @@ public abstract class SmartSuggestionsService extends Service { pw.print(prefix); pw.println("smartSuggestion:"); mSmartSuggestion.dump(prefix2, pw); } + if (mOnSuccessTime > 0) { + final long responseTime = mOnSuccessTime - mRequestTime; + pw.print(prefix); pw.print("response time: "); + TimeUtils.formatDuration(responseTime, pw); pw.println(); + } + + if (mUiFirstShownTime > 0) { + final long uiRenderingTime = mUiFirstShownTime - mRequestTime; + pw.print(prefix); pw.print("UI rendering time: "); + TimeUtils.formatDuration(uiRenderingTime, pw); pw.println(); + } + + if (mUiFirstDestroyedTime > 0) { + final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime; + pw.print(prefix); pw.print("UI life time: "); + TimeUtils.formatDuration(uiTotalTime, pw); pw.println(); + } } private void destroy() { diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 3ee5f1fcc883..33b3ff4fef59 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Message; @@ -225,7 +226,7 @@ public class GestureDetector { private int mMinimumFlingVelocity; private int mMaximumFlingVelocity; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); @@ -592,8 +593,8 @@ public class GestureDetector { if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, - mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT); + mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + + ViewConfiguration.getLongPressTimeout()); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 8b39cc72af8f..9cced4e0052f 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1803,6 +1803,28 @@ public class KeyEvent extends InputEvent implements Parcelable { } /** + * Returns whether this key will be sent to the + * {@link android.media.session.MediaSession.Callback} if not handled. + */ + public static final boolean isMediaSessionKey(int keyCode) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + return true; + } + return false; + } + + /** * Returns true if the specified keycode is a gamepad button. * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}. */ @@ -1861,31 +1883,6 @@ public class KeyEvent extends InputEvent implements Parcelable { } } - /** - * Returns whether this key can be handled by - * {@link android.media.session.MediaSession.Callback}. - * - * @hide - */ - public static final boolean isMediaSessionKey(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - return true; - } - return false; - } - - /** Is this a system key? System keys can not be used for menu shortcuts. * @hide */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4b9cbff8c161..bd2aa64b33a1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -112,6 +112,9 @@ import android.view.autofill.AutofillValue; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.view.inspector.InspectableProperty; +import android.view.inspector.InspectableProperty.EnumMap; +import android.view.inspector.InspectableProperty.FlagMap; import android.view.intelligence.ContentCaptureManager; import android.widget.Checkable; import android.widget.FrameLayout; @@ -4895,6 +4898,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int LAYER_TYPE_HARDWARE = 2; + /** @hide */ + @IntDef(prefix = { "LAYER_TYPE_" }, value = { + LAYER_TYPE_NONE, + LAYER_TYPE_SOFTWARE, + LAYER_TYPE_HARDWARE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LayerType {} + @ViewDebug.ExportedProperty(category = "drawing", mapping = { @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"), @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"), @@ -6516,6 +6528,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return a bitmask representing the enabled scroll indicators */ + @InspectableProperty(flagMapping = { + @FlagMap(target = SCROLL_INDICATORS_NONE, mask = 0xffff_ffff, name = "none"), + @FlagMap(target = SCROLL_INDICATOR_TOP, name = "top"), + @FlagMap(target = SCROLL_INDICATOR_BOTTOM, name = "bottom"), + @FlagMap(target = SCROLL_INDICATOR_LEFT, name = "left"), + @FlagMap(target = SCROLL_INDICATOR_RIGHT, name = "right"), + @FlagMap(target = SCROLL_INDICATOR_START, name = "start"), + @FlagMap(target = SCROLL_INDICATOR_END, name = "end") + }) @ScrollIndicators public int getScrollIndicators() { return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK) @@ -7582,7 +7603,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * {@see #setAccessibilityPaneTitle}. */ - @Nullable public CharSequence getAccessibilityPaneTitle() { + @InspectableProperty + @Nullable + public CharSequence getAccessibilityPaneTitle() { return mAccessibilityPaneTitle; } @@ -8580,6 +8603,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_autofillHints */ @ViewDebug.ExportedProperty() + @InspectableProperty @Nullable public String[] getAutofillHints() { return mAutofillHints; } @@ -8624,6 +8648,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, to = "yesExcludeDescendants"), @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to = "noExcludeDescendants")}) + @InspectableProperty(enumMapping = { + @EnumMap(value = IMPORTANT_FOR_AUTOFILL_AUTO, name = "auto"), + @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES, name = "yes"), + @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO, name = "no"), + @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, + name = "yesExcludeDescendants"), + @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, + name = "noExcludeDescendants"), + }) public @AutofillImportance int getImportantForAutofill() { return (mPrivateFlags3 & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT; @@ -8808,6 +8841,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, to = "yesExcludeDescendants"), @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to = "noExcludeDescendants")}) + @InspectableProperty(enumMapping = { + @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"), + @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"), + @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"), + @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, + name = "yesExcludeDescendants"), + @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, + name = "noExcludeDescendants"), + }) public @ContentCaptureImportance int getImportantForContentCapture() { // NOTE: the important for content capture values were the first flags added and are set in // the rightmost position, so we don't need to shift them @@ -9619,6 +9661,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_contentDescription */ @ViewDebug.ExportedProperty(category = "accessibility") + @InspectableProperty public CharSequence getContentDescription() { return mContentDescription; } @@ -9699,6 +9742,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setAccessibilityTraversalBefore(int) */ + @InspectableProperty public int getAccessibilityTraversalBefore() { return mAccessibilityTraversalBeforeId; } @@ -9743,6 +9787,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setAccessibilityTraversalAfter(int) */ + @InspectableProperty public int getAccessibilityTraversalAfter() { return mAccessibilityTraversalAfterId; } @@ -9754,6 +9799,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The labeled view id. */ @ViewDebug.ExportedProperty(category = "accessibility") + @InspectableProperty public int getLabelFor() { return mLabelForId; } @@ -9817,6 +9863,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if this view has focus, false otherwise. */ @ViewDebug.ExportedProperty(category = "focus") + @InspectableProperty(hasAttributeId = false) public boolean isFocused() { return (mPrivateFlags & PFLAG_FOCUSED) != 0; } @@ -9841,6 +9888,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_isScrollContainer */ + @InspectableProperty(name = "isScrollContainer") public boolean isScrollContainer() { return (mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0; } @@ -9896,6 +9944,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @Deprecated @DrawingCacheQuality + @InspectableProperty(enumMapping = { + @EnumMap(value = DRAWING_CACHE_QUALITY_LOW, name = "low"), + @EnumMap(value = DRAWING_CACHE_QUALITY_HIGH, name = "high"), + @EnumMap(value = DRAWING_CACHE_QUALITY_AUTO, name = "auto") + }) public int getDrawingCacheQuality() { return mViewFlags & DRAWING_CACHE_QUALITY_MASK; } @@ -9941,6 +9994,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_keepScreenOn */ + @InspectableProperty public boolean getKeepScreenOn() { return (mViewFlags & KEEP_SCREEN_ON) != 0; } @@ -9965,6 +10019,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextFocusLeft */ + @InspectableProperty(name = "nextFocusLeft") public int getNextFocusLeftId() { return mNextFocusLeftId; } @@ -9986,6 +10041,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextFocusRight */ + @InspectableProperty(name = "nextFocusRight") public int getNextFocusRightId() { return mNextFocusRightId; } @@ -10007,6 +10063,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextFocusUp */ + @InspectableProperty(name = "nextFocusUp") public int getNextFocusUpId() { return mNextFocusUpId; } @@ -10028,6 +10085,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextFocusDown */ + @InspectableProperty(name = "nextFocusDown") public int getNextFocusDownId() { return mNextFocusDownId; } @@ -10049,6 +10107,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextFocusForward */ + @InspectableProperty(name = "nextFocusForward") public int getNextFocusForwardId() { return mNextFocusForwardId; } @@ -10071,6 +10130,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_nextClusterForward */ + @InspectableProperty(name = "nextClusterForward") public int getNextClusterForwardId() { return mNextClusterForwardId; } @@ -10437,6 +10497,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setSystemUiVisibility(int) */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean getFitsSystemWindows() { return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS; } @@ -10498,6 +10559,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"), @ViewDebug.IntToString(from = GONE, to = "GONE") }) + @InspectableProperty(enumMapping = { + @EnumMap(value = VISIBLE, name = "visible"), + @EnumMap(value = INVISIBLE, name = "invisible"), + @EnumMap(value = GONE, name = "gone") + }) @Visibility public int getVisibility() { return mViewFlags & VISIBILITY_MASK; @@ -10521,6 +10587,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if this view is enabled, false otherwise. */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean isEnabled() { return (mViewFlags & ENABLED_MASK) == ENABLED; } @@ -10690,6 +10757,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_soundEffectsEnabled */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean isSoundEffectsEnabled() { return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED); } @@ -10719,6 +10787,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_hapticFeedbackEnabled */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean isHapticFeedbackEnabled() { return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED); } @@ -10741,6 +10810,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE") }) + @InspectableProperty(hasAttributeId = false, enumMapping = { + @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"), + @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl"), + @EnumMap(value = LAYOUT_DIRECTION_INHERIT, name = "inherit"), + @EnumMap(value = LAYOUT_DIRECTION_LOCALE, name = "locale") + }) @LayoutDir public int getRawLayoutDirection() { return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; @@ -10794,6 +10869,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL") }) + @InspectableProperty(enumMapping = { + @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"), + @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl") + }) @ResolvedLayoutDir public int getLayoutDirection() { final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; @@ -10980,6 +11059,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_clickable */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean isClickable() { return (mViewFlags & CLICKABLE) == CLICKABLE; } @@ -11007,6 +11087,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setLongClickable(boolean) * @attr ref android.R.styleable#View_longClickable */ + @InspectableProperty public boolean isLongClickable() { return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE; } @@ -11032,6 +11113,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setContextClickable(boolean) * @attr ref android.R.styleable#View_contextClickable */ + @InspectableProperty public boolean isContextClickable() { return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; } @@ -11111,6 +11193,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the view is currently pressed, false otherwise */ @ViewDebug.ExportedProperty + @InspectableProperty(hasAttributeId = false) public boolean isPressed() { return (mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED; } @@ -11126,6 +11209,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setAssistBlocked(boolean) * @attr ref android.R.styleable#View_assistBlocked */ + @InspectableProperty public boolean isAssistBlocked() { return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0; } @@ -11163,6 +11247,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setSaveEnabled(boolean) * @attr ref android.R.styleable#View_saveEnabled */ + @InspectableProperty public boolean isSaveEnabled() { return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED; } @@ -11198,6 +11283,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_filterTouchesWhenObscured */ @ViewDebug.ExportedProperty + @InspectableProperty public boolean getFilterTouchesWhenObscured() { return (mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0; } @@ -11270,6 +11356,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"), @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO") }, category = "focus") + @InspectableProperty(enumMapping = { + @EnumMap(value = NOT_FOCUSABLE, name = "false"), + @EnumMap(value = FOCUSABLE, name = "true"), + @EnumMap(value = FOCUSABLE_AUTO, name = "auto") + }) @Focusable public int getFocusable() { return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE; @@ -11284,6 +11375,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_focusableInTouchMode */ @ViewDebug.ExportedProperty(category = "focus") + @InspectableProperty public final boolean isFocusableInTouchMode() { return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE); } @@ -11295,6 +11387,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return Whether the view should be treated as a focusable unit by screen reader. */ + @InspectableProperty public boolean isScreenReaderFocusable() { return (mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE) != 0; } @@ -11323,6 +11416,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_accessibilityHeading */ + @InspectableProperty public boolean isAccessibilityHeading() { return (mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING) != 0; } @@ -11377,6 +11471,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_keyboardNavigationCluster */ @ViewDebug.ExportedProperty(category = "focus") + @InspectableProperty public final boolean isKeyboardNavigationCluster() { return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0; } @@ -11486,6 +11581,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_focusedByDefault */ @ViewDebug.ExportedProperty(category = "focus") + @InspectableProperty public final boolean isFocusedByDefault() { return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0; } @@ -11599,6 +11695,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled */ @ViewDebug.ExportedProperty(category = "focus") + @InspectableProperty public final boolean getDefaultFocusHighlightEnabled() { return mDefaultFocusHighlightEnabled; } @@ -11817,6 +11914,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return True if this View is accessibility focused. */ + @InspectableProperty(hasAttributeId = false) public boolean isAccessibilityFocused() { return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0; } @@ -12143,6 +12241,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to = "noHideDescendants") }) + @InspectableProperty(enumMapping = { + @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_AUTO, name = "auto"), + @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_YES, name = "yes"), + @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO, name = "no"), + @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, + name = "noHideDescendants"), + }) public int getImportantForAccessibility() { return (mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK) >> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT; @@ -12195,6 +12300,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setAccessibilityLiveRegion(int) */ + @InspectableProperty(enumMapping = { + @EnumMap(value = ACCESSIBILITY_LIVE_REGION_NONE, name = "none"), + @EnumMap(value = ACCESSIBILITY_LIVE_REGION_POLITE, name = "polite"), + @EnumMap(value = ACCESSIBILITY_LIVE_REGION_ASSERTIVE, name = "assertive") + }) public int getAccessibilityLiveRegion() { return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK) >> PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT; @@ -15042,6 +15152,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The left edge of the displayed part of your view, in pixels. */ + @InspectableProperty public final int getScrollX() { return mScrollX; } @@ -15053,6 +15164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The top edge of the displayed part of your view, in pixels. */ + @InspectableProperty public final int getScrollY() { return mScrollY; } @@ -15289,6 +15401,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The degrees of rotation. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getRotation() { return mRenderNode.getRotation(); } @@ -15329,6 +15442,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The degrees of Y rotation. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getRotationY() { return mRenderNode.getRotationY(); } @@ -15373,6 +15487,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The degrees of X rotation. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getRotationX() { return mRenderNode.getRotationX(); } @@ -15418,6 +15533,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The scaling factor. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getScaleX() { return mRenderNode.getScaleX(); } @@ -15455,6 +15571,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The scaling factor. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getScaleY() { return mRenderNode.getScaleY(); } @@ -15494,6 +15611,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_transformPivotX */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty(name = "transformPivotX") public float getPivotX() { return mRenderNode.getPivotX(); } @@ -15536,6 +15654,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_transformPivotY */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty(name = "transformPivotY") public float getPivotY() { return mRenderNode.getPivotY(); } @@ -15594,6 +15713,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The opacity of the view. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getAlpha() { return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1; } @@ -15816,6 +15936,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if force dark is allowed (default), false if it is disabled */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public boolean isForceDarkAllowed() { return mRenderNode.isForceDarkAllowed(); } @@ -16187,6 +16308,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The base depth position of the view, in pixels. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getElevation() { return mRenderNode.getElevation(); } @@ -16215,6 +16337,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The horizontal position of this view relative to its left position, in pixels. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getTranslationX() { return mRenderNode.getTranslationX(); } @@ -16249,6 +16372,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * in pixels. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getTranslationY() { return mRenderNode.getTranslationY(); } @@ -16280,6 +16404,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The depth of this view relative to its elevation. */ @ViewDebug.ExportedProperty(category = "drawing") + @InspectableProperty public float getTranslationZ() { return mRenderNode.getTranslationZ(); } @@ -16324,6 +16449,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return StateListAnimator or null if it does not exists * @see #setStateListAnimator(android.animation.StateListAnimator) */ + @InspectableProperty public StateListAnimator getStateListAnimator() { return mStateListAnimator; } @@ -16437,6 +16563,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setOutlineProvider(ViewOutlineProvider) */ + @InspectableProperty public ViewOutlineProvider getOutlineProvider() { return mOutlineProvider; } @@ -16513,6 +16640,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing * was set */ + @InspectableProperty public @ColorInt int getOutlineSpotShadowColor() { return mRenderNode.getSpotShadowColor(); } @@ -16541,6 +16669,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if * nothing was set */ + @InspectableProperty public @ColorInt int getOutlineAmbientShadowColor() { return mRenderNode.getAmbientShadowColor(); } @@ -17720,6 +17849,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Get the fading edge flags, used for inspection. + * + * @return One of {@link #FADING_EDGE_NONE}, {@link #FADING_EDGE_VERTICAL}, + * or {@link #FADING_EDGE_HORIZONTAL} + * @hide + */ + @InspectableProperty(name = "requiresFadingEdge", flagMapping = { + @FlagMap(target = FADING_EDGE_NONE, mask = FADING_EDGE_MASK, name = "none"), + @FlagMap(target = FADING_EDGE_VERTICAL, name = "vertical"), + @FlagMap(target = FADING_EDGE_HORIZONTAL, name = "horizontal") + }) + int getFadingEdge() { + return mViewFlags & FADING_EDGE_MASK; + } + + /** + * Get the fading edge length, used for inspection + * + * @return The fading edge length or 0 + * @hide + */ + @InspectableProperty + int getFadingEdgeLength() { + if (mScrollCache != null && (mViewFlags & FADING_EDGE_MASK) != FADING_EDGE_NONE) { + return mScrollCache.fadingEdgeLength; + } + return 0; + } + + /** * Returns the strength, or intensity, of the top faded edge. The strength is * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation * returns 0.0 or 1.0 but no value in between. @@ -17883,6 +18042,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade */ + @InspectableProperty public int getScrollBarDefaultDelayBeforeFade() { return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() : mScrollCache.scrollBarDefaultDelayBeforeFade; @@ -17907,6 +18067,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scrollbarFadeDuration */ + @InspectableProperty public int getScrollBarFadeDuration() { return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() : mScrollCache.scrollBarFadeDuration; @@ -17931,6 +18092,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scrollbarSize */ + @InspectableProperty public int getScrollBarSize() { return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() : mScrollCache.scrollBarSize; @@ -17990,6 +18152,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_OVERLAY, to = "OUTSIDE_OVERLAY"), @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_INSET, to = "OUTSIDE_INSET") }) + @InspectableProperty(enumMapping = { + @EnumMap(value = SCROLLBARS_INSIDE_OVERLAY, name = "insideOverlay"), + @EnumMap(value = SCROLLBARS_INSIDE_INSET, name = "insideInset"), + @EnumMap(value = SCROLLBARS_OUTSIDE_OVERLAY, name = "outsideOverlay"), + @EnumMap(value = SCROLLBARS_OUTSIDE_INSET, name = "outsideInset") + }) @ScrollBarStyle public int getScrollBarStyle() { return mViewFlags & SCROLLBARS_STYLE_MASK; @@ -19411,6 +19579,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getDrawableState() * @see #setDuplicateParentStateEnabled(boolean) */ + @InspectableProperty(name = "duplicateParentState") public boolean isDuplicateParentStateEnabled() { return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE; } @@ -19452,7 +19621,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_layerType */ - public void setLayerType(int layerType, @Nullable Paint paint) { + public void setLayerType(@LayerType int layerType, @Nullable Paint paint) { if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) { throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE"); @@ -19536,6 +19705,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE */ + @InspectableProperty(enumMapping = { + @EnumMap(value = LAYER_TYPE_NONE, name = "none"), + @EnumMap(value = LAYER_TYPE_SOFTWARE, name = "software"), + @EnumMap(value = LAYER_TYPE_HARDWARE, name = "hardware") + }) + @LayerType public int getLayerType() { return mLayerType; } @@ -22312,6 +22487,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_background */ + @InspectableProperty public Drawable getBackground() { return mBackground; } @@ -22347,6 +22523,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_backgroundTint * @see #setBackgroundTintList(ColorStateList) */ + @InspectableProperty(name = "backgroundTint") @Nullable public ColorStateList getBackgroundTintList() { return mBackgroundTint != null ? mBackgroundTint.mTintList : null; @@ -22383,6 +22560,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setBackgroundTintMode(PorterDuff.Mode) */ @Nullable + @InspectableProperty public PorterDuff.Mode getBackgroundTintMode() { return mBackgroundTint != null ? mBackgroundTint.mTintMode : null; } @@ -22418,6 +22596,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #onDrawForeground(Canvas) */ + @InspectableProperty public Drawable getForeground() { return mForegroundInfo != null ? mForegroundInfo.mDrawable : null; } @@ -22496,6 +22675,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_foregroundGravity */ + @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY) public int getForegroundGravity() { return mForegroundInfo != null ? mForegroundInfo.mGravity : Gravity.START | Gravity.TOP; @@ -22563,6 +22743,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_foregroundTint * @see #setForegroundTintList(ColorStateList) */ + @InspectableProperty(name = "foregroundTint") @Nullable public ColorStateList getForegroundTintList() { return mForegroundInfo != null && mForegroundInfo.mTintInfo != null @@ -22602,6 +22783,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_foregroundTintMode * @see #setForegroundTintMode(PorterDuff.Mode) */ + @InspectableProperty @Nullable public PorterDuff.Mode getForegroundTintMode() { return mForegroundInfo != null && mForegroundInfo.mTintInfo != null @@ -22846,6 +23028,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return the top padding in pixels */ + @InspectableProperty public int getPaddingTop() { return mPaddingTop; } @@ -22857,6 +23040,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return the bottom padding in pixels */ + @InspectableProperty public int getPaddingBottom() { return mPaddingBottom; } @@ -22868,6 +23052,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return the left padding in pixels */ + @InspectableProperty public int getPaddingLeft() { if (!isPaddingResolved()) { resolvePadding(); @@ -22897,6 +23082,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return the right padding in pixels */ + @InspectableProperty public int getPaddingRight() { if (!isPaddingResolved()) { resolvePadding(); @@ -23019,6 +23205,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the view is selected, false otherwise */ @ViewDebug.ExportedProperty + @InspectableProperty(hasAttributeId = false) public boolean isSelected() { return (mPrivateFlags & PFLAG_SELECTED) != 0; } @@ -23062,6 +23249,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the view is activated, false otherwise */ @ViewDebug.ExportedProperty + @InspectableProperty(hasAttributeId = false) public boolean isActivated() { return (mPrivateFlags & PFLAG_ACTIVATED) != 0; } @@ -23560,6 +23748,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @IdRes @ViewDebug.CapturedViewProperty + @InspectableProperty public int getId() { return mID; } @@ -23584,6 +23773,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getTag(int) */ @ViewDebug.ExportedProperty + @InspectableProperty public Object getTag() { return mTag; } @@ -23780,6 +23970,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * if baseline alignment is not supported */ @ViewDebug.ExportedProperty(category = "layout") + @InspectableProperty public int getBaseline() { return -1; } @@ -24150,6 +24341,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_minHeight */ + @InspectableProperty(name = "minHeight") public int getMinimumHeight() { return mMinHeight; } @@ -24180,6 +24372,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_minWidth */ + @InspectableProperty(name = "minWidth") public int getMinimumWidth() { return mMinWidth; } @@ -25199,6 +25392,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return This view's over-scroll mode. */ + @InspectableProperty(enumMapping = { + @EnumMap(value = OVER_SCROLL_ALWAYS, name = "always"), + @EnumMap(value = OVER_SCROLL_IF_CONTENT_SCROLLS, name = "ifContentScrolls"), + @EnumMap(value = OVER_SCROLL_NEVER, name = "never") + }) public int getOverScrollMode() { return mOverScrollMode; } @@ -25257,6 +25455,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setNestedScrollingEnabled(boolean) */ + @InspectableProperty public boolean isNestedScrollingEnabled() { return (mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED) == PFLAG3_NESTED_SCROLLING_ENABLED; @@ -25586,6 +25785,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL") }) + @InspectableProperty(hasAttributeId = false, enumMapping = { + @EnumMap(value = TEXT_DIRECTION_INHERIT, name = "inherit"), + @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"), + @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"), + @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"), + @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"), + }) @UnsupportedAppUsage public int getRawTextDirection() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK) >> PFLAG2_TEXT_DIRECTION_MASK_SHIFT; @@ -25653,6 +25862,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL") }) + @InspectableProperty(hasAttributeId = false, enumMapping = { + @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"), + @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"), + @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"), + @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"), + @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"), + }) public int getTextDirection() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; } @@ -25824,6 +26042,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END") }) + @InspectableProperty(hasAttributeId = false, enumMapping = { + @EnumMap(value = TEXT_ALIGNMENT_INHERIT, name = "inherit"), + @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"), + @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"), + @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"), + @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"), + @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"), + @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd") + }) @TextAlignment @UnsupportedAppUsage public int getRawTextAlignment() { @@ -25890,6 +26117,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"), @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END") }) + @InspectableProperty(enumMapping = { + @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"), + @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"), + @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"), + @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"), + @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"), + @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd") + }) @TextAlignment public int getTextAlignment() { return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK) >> @@ -26122,6 +26357,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Gets the pointer icon for the current view. */ + @InspectableProperty public PointerIcon getPointerIcon() { return mPointerIcon; } @@ -26672,6 +26908,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * if no name has been given. */ @ViewDebug.ExportedProperty + @InspectableProperty public String getTransitionName() { return mTransitionName; } @@ -28277,6 +28514,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setTooltipText(CharSequence) * @attr ref android.R.styleable#View_tooltipText */ + @InspectableProperty @Nullable public CharSequence getTooltipText() { return mTooltipInfo != null ? mTooltipInfo.mTooltipText : null; diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java index 8df83c0e5dff..797b861e9e70 100644 --- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java +++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java @@ -76,7 +76,9 @@ public final class ActionsSuggestionsHelper { return new ActionsSuggestionsModel.ConversationMessage[]{ new ActionsSuggestionsModel.ConversationMessage( FIRST_NON_LOCAL_USER, - lastMessage.getText().toString())}; + lastMessage.getText().toString(), + 0, + null)}; } // Encode the messages in the reverse order, stop whenever the Person object is missing. @@ -89,7 +91,7 @@ public final class ActionsSuggestionsHelper { } nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage( personEncoder.encode(message.getAuthor()), - message.getText().toString())); + message.getText().toString(), 0, null)); } return nativeMessages.toArray( new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]); diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java index b37e1b8962c5..4d2c04663579 100644 --- a/core/java/android/webkit/TokenBindingService.java +++ b/core/java/android/webkit/TokenBindingService.java @@ -16,12 +16,7 @@ package android.webkit; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; -import android.net.Uri; - -import java.security.KeyPair; /** * Enables the token binding procotol, and provides access to the keys. See @@ -30,86 +25,9 @@ import java.security.KeyPair; * All methods are required to be called on the UI thread where WebView is * attached to the View hierarchy. * @hide + * @deprecated this is no longer supported. */ @SystemApi +@Deprecated public abstract class TokenBindingService { - - public static final String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5"; - public static final String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS"; - public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256"; - - /** - * Provides the KeyPair information. - */ - public static abstract class TokenBindingKey { - /** - * The public, private key pair. - */ - public abstract KeyPair getKeyPair(); - - /** - * The algorithm that is used to generate the key pair. - */ - public abstract String getAlgorithm(); - } - - /** - * Returns the default TokenBinding service instance. At present there is - * only one token binding service instance for all WebView instances, - * however this restriction may be relaxed in the future. - * - * @return The default TokenBindingService instance. - */ - public static TokenBindingService getInstance() { - return WebViewFactory.getProvider().getTokenBindingService(); - } - - /** - * Enables the token binding protocol. The token binding protocol - * has to be enabled before creating any WebViews. - * - * @throws IllegalStateException if a WebView was already created. - */ - public abstract void enableTokenBinding(); - - /** - * Retrieves the key pair for a given origin from the internal - * TokenBinding key store asynchronously. - * - * The user can provide a list of acceptable algorithms for the retrieved - * key pair. If a key pair exists and it is in the list of algorithms, then - * the key is returned. If it is not in the list, no key is returned. - * - * If no key pair exists, WebView chooses an algorithm from the list, in - * the order given, to generate a key. - * - * The user can pass {@code null} if any algorithm is acceptable. - * - * @param origin The origin for the server. - * @param algorithm The list of algorithms. An IllegalArgumentException is thrown if array is - * empty. - * @param callback The callback that will be called when key is available. - */ - public abstract void getKey(Uri origin, - @Nullable String[] algorithm, - @NonNull ValueCallback<TokenBindingKey> callback); - /** - * Deletes specified key (for use when associated cookie is cleared). - * - * @param origin The origin of the server. - * @param callback The callback that will be called when key is deleted. The - * callback parameter (Boolean) will indicate if operation is - * successful or if failed. - */ - public abstract void deleteKey(Uri origin, - @Nullable ValueCallback<Boolean> callback); - - /** - * Deletes all the keys (for use when cookies are cleared). - * - * @param callback The callback that will be called when keys are deleted. - * The callback parameter (Boolean) will indicate if operation is - * successful or if failed. - */ - public abstract void deleteAllKeys(@Nullable ValueCallback<Boolean> callback); } diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 4ff49ea1bbc3..6a1ed39e25b3 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -129,7 +129,8 @@ public interface WebViewFactoryProvider { * Gets the TokenBindingService instance for this WebView implementation. The * implementation must return the same instance on subsequent calls. * - * @return the TokenBindingService instance + * @deprecated this method only returns {@code null} + * @return the TokenBindingService instance (which is always {@code null}) */ TokenBindingService getTokenBindingService(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c6155ced9c9f..3c32bb29cf66 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2051,8 +2051,8 @@ public class Editor { } void updateCursorPosition() { - if (mTextView.mCursorDrawableRes == 0) { - mDrawableForCursor = null; + loadCursorDrawable(); + if (mDrawableForCursor == null) { return; } @@ -2462,10 +2462,7 @@ public class Editor { } private void updateCursorPosition(int top, int bottom, float horizontal) { - if (mDrawableForCursor == null) { - mDrawableForCursor = mTextView.getContext().getDrawable( - mTextView.mCursorDrawableRes); - } + loadCursorDrawable(); final int left = clampHorizontalPosition(mDrawableForCursor, horizontal); final int width = mDrawableForCursor.getIntrinsicWidth(); mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width, @@ -5698,6 +5695,12 @@ public class Editor { public boolean isActive(); } + void loadCursorDrawable() { + if (mDrawableForCursor == null) { + mDrawableForCursor = mTextView.getTextCursorDrawable(); + } + } + private class InsertionPointCursorController implements CursorController { private InsertionHandleView mHandle; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7b39efed0c3a..2dec4e87e662 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -18,6 +18,8 @@ package android.widget; import android.annotation.ColorInt; import android.annotation.DimenRes; +import android.annotation.IntDef; +import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.StyleRes; import android.annotation.UnsupportedAppUsage; @@ -131,6 +133,12 @@ public class RemoteViews implements Parcelable, Filter { static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId"; /** + * The intent extra that contains {@code true} if inflating as dak text theme. + * @hide + */ + static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground"; + + /** * The intent extra that contains the bounds for all shared elements. */ public static final String EXTRA_SHARED_ELEMENT_BOUNDS = @@ -163,6 +171,36 @@ public class RemoteViews implements Parcelable, Filter { private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21; private static final int SET_INT_TAG_TAG = 22; + /** @hide **/ + @IntDef(flag = true, value = { + FLAG_REAPPLY_DISALLOWED, + FLAG_WIDGET_IS_COLLECTION_CHILD, + FLAG_USE_LIGHT_BACKGROUND_LAYOUT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApplyFlags {} + /** + * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify + * the layout in a way that isn't recoverable, since views are being removed. + * @hide + */ + public static final int FLAG_REAPPLY_DISALLOWED = 1; + /** + * This flag indicates whether this RemoteViews object is being created from a + * RemoteViewsService for use as a child of a widget collection. This flag is used + * to determine whether or not certain features are available, in particular, + * setting on click extras and setting on click pending intents. The former is enabled, + * and the latter disabled when this flag is true. + * @hide + */ + public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2; + /** + * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead + * of {link #mLayoutId} + * @hide + */ + public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4; + /** * Application that hosts the remote views. * @@ -178,6 +216,11 @@ public class RemoteViews implements Parcelable, Filter { private final int mLayoutId; /** + * The resource ID of the layout file in dark text mode. (Added to the parcel) + */ + private int mLightBackgroundLayoutId = 0; + + /** * An array of actions to perform on the view tree once it has been * inflated */ @@ -197,12 +240,6 @@ public class RemoteViews implements Parcelable, Filter { private boolean mIsRoot = true; /** - * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify - * the layout in a way that isn't recoverable, since views are being removed. - */ - private boolean mReapplyDisallowed; - - /** * Constants to whether or not this RemoteViews is composed of a landscape and portrait * RemoteViews. */ @@ -218,14 +255,8 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage private RemoteViews mPortrait = null; - /** - * This flag indicates whether this RemoteViews object is being created from a - * RemoteViewsService for use as a child of a widget collection. This flag is used - * to determine whether or not certain features are available, in particular, - * setting on click extras and setting on click pending intents. The former is enabled, - * and the latter disabled when this flag is true. - */ - private boolean mIsWidgetCollectionChild = false; + @ApplyFlags + private int mApplyFlags = 0; /** Class cookies of the Parcel this instance was read from. */ private final Map<Class, Object> mClassCookies; @@ -289,18 +320,15 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - public void setReapplyDisallowed() { - mReapplyDisallowed = true; + public void addFlags(@ApplyFlags int flags) { + mApplyFlags = mApplyFlags | flags; } /** - * @return Whether it is disallowed to reapply another remoteview with the same layout as this - * view. True if this remoteview has actions that destroyed view tree of the base layout. - * * @hide */ - public boolean isReapplyDisallowed() { - return mReapplyDisallowed; + public boolean hasFlags(@ApplyFlags int flag) { + return (mApplyFlags & flag) == flag; } /** @@ -768,7 +796,10 @@ public class RemoteViews implements Parcelable, Filter { // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent // RemoteViewsService AppWidgetHostView host = (AppWidgetHostView) rootParent; - intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()); + intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) + .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, + hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)); + if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; v.setRemoteViewsAdapter(intent, isAsync); @@ -829,7 +860,7 @@ public class RemoteViews implements Parcelable, Filter { // If the view is an AdapterView, setting a PendingIntent on click doesn't make // much sense, do they mean to set a PendingIntent template for the // AdapterView's children? - if (mIsWidgetCollectionChild) { + if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item " + "(id: " + viewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); @@ -843,7 +874,7 @@ public class RemoteViews implements Parcelable, Filter { } target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent); } else if (mResponse.mFillIntent != null) { - if (!mIsWidgetCollectionChild) { + if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " + "only from RemoteViewsFactory (ie. on collection items)."); return; @@ -1545,6 +1576,7 @@ public class RemoteViews implements Parcelable, Filter { viewId = parcel.readInt(); mIndex = parcel.readInt(); mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies); + mNestedViews.addFlags(mApplyFlags); } public void writeToParcel(Parcel dest, int flags) { @@ -2190,7 +2222,7 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - public RemoteViews(String packageName, int userId, int layoutId) { + public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) { this(getApplicationInfo(packageName, userId), layoutId); } @@ -2203,7 +2235,7 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - protected RemoteViews(ApplicationInfo application, int layoutId) { + protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) { mApplication = application; mLayoutId = layoutId; mBitmapCache = new BitmapCache(); @@ -2229,7 +2261,8 @@ public class RemoteViews implements Parcelable, Filter { throw new RuntimeException("Both RemoteViews must share the same package and user"); } mApplication = portrait.mApplication; - mLayoutId = portrait.getLayoutId(); + mLayoutId = portrait.mLayoutId; + mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId; mLandscape = landscape; mPortrait = portrait; @@ -2250,8 +2283,8 @@ public class RemoteViews implements Parcelable, Filter { mApplication = src.mApplication; mIsRoot = src.mIsRoot; mLayoutId = src.mLayoutId; - mIsWidgetCollectionChild = src.mIsWidgetCollectionChild; - mReapplyDisallowed = src.mReapplyDisallowed; + mLightBackgroundLayoutId = src.mLightBackgroundLayoutId; + mApplyFlags = src.mApplyFlags; mClassCookies = src.mClassCookies; if (src.hasLandscapeAndPortraitLayouts()) { @@ -2309,7 +2342,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); - mIsWidgetCollectionChild = parcel.readInt() == 1; + mLightBackgroundLayoutId = parcel.readInt(); readActionsFromParcel(parcel, depth); } else { @@ -2318,9 +2351,10 @@ public class RemoteViews implements Parcelable, Filter { mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth, mClassCookies); mApplication = mPortrait.mApplication; - mLayoutId = mPortrait.getLayoutId(); + mLayoutId = mPortrait.mLayoutId; + mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId; } - mReapplyDisallowed = parcel.readInt() == 0; + mApplyFlags = parcel.readInt(); } private void readActionsFromParcel(Parcel parcel, int depth) { @@ -2409,19 +2443,8 @@ public class RemoteViews implements Parcelable, Filter { * @return the layout id. */ public int getLayoutId() { - return mLayoutId; - } - - /* - * This flag indicates whether this RemoteViews object is being created from a - * RemoteViewsService for use as a child of a widget collection. This flag is used - * to determine whether or not certain features are available, in particular, - * setting on click extras and setting on click pending intents. The former is enabled, - * and the latter disabled when this flag is true. - */ - @UnsupportedAppUsage - void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) { - mIsWidgetCollectionChild = isWidgetCollectionChild; + return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0) + ? mLightBackgroundLayoutId : mLayoutId; } /** @@ -3292,6 +3315,33 @@ public class RemoteViews implements Parcelable, Filter { setInt(viewId, "setLabelFor", labeledId); } + /** + * Provides an alternate layout ID, which can be used to inflate this view. This layout will be + * used by the host when the widgets displayed on a light-background where foreground elements + * and text can safely draw using a dark color without any additional background protection. + */ + public void setLightBackgroundLayoutId(@LayoutRes int layoutId) { + mLightBackgroundLayoutId = layoutId; + } + + /** + * If this view supports dark text versions, creates a copy representing that version, + * otherwise returns itself. + * @hide + */ + public RemoteViews getDarkTextViews() { + if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) { + return this; + } + + try { + addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT); + return new RemoteViews(this); + } finally { + mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT; + } + } + private RemoteViews getRemoteViewsToApply(Context context) { if (hasLandscapeAndPortraitLayouts()) { int orientation = context.getResources().getConfiguration().orientation; @@ -3373,6 +3423,12 @@ public class RemoteViews implements Parcelable, Filter { * @hide */ public interface OnViewAppliedListener { + /** + * Callback when the RemoteView has finished inflating, + * but no actions have been applied yet. + */ + default void onViewInflated(View v) {}; + void onViewApplied(View v); void onError(Exception e); @@ -3469,6 +3525,10 @@ public class RemoteViews implements Parcelable, Filter { @Override protected void onPostExecute(ViewTree viewTree) { if (mError == null) { + if (mListener != null) { + mListener.onViewInflated(viewTree.mRoot); + } + try { if (mActions != null) { OnClickHandler handler = mHandler == null @@ -3652,7 +3712,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication.writeToParcel(dest, flags); } dest.writeInt(mLayoutId); - dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); + dest.writeInt(mLightBackgroundLayoutId); writeActionsToParcel(dest); } else { dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); @@ -3665,7 +3725,7 @@ public class RemoteViews implements Parcelable, Filter { // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } - dest.writeInt(mReapplyDisallowed ? 1 : 0); + dest.writeInt(mApplyFlags); } private void writeActionsToParcel(Parcel parcel) { diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index d17c7c58ee74..c5cd1a1ece35 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -16,6 +16,9 @@ package android.widget; +import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID; +import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND; + import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; import android.app.IServiceConnection; @@ -97,6 +100,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private final Context mContext; private final Intent mIntent; private final int mAppWidgetId; + private final boolean mOnLightBackground; private final Executor mAsyncViewLoadExecutor; private OnClickHandler mRemoteViewsOnClickHandler; @@ -817,13 +821,13 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback throw new IllegalArgumentException("Non-null Intent must be specified."); } - mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); + mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); mRequestedViews = new RemoteViewsFrameLayoutRefSet(); + mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false); // Strip the previously injected app widget id from service intent - if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { - intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); - } + intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID); + intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND); // Initialize the worker thread mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); @@ -1107,6 +1111,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } else { layout = new RemoteViewsFrameLayout(parent.getContext(), mCache); layout.setExecutor(mAsyncViewLoadExecutor); + layout.setOnLightBackground(mOnLightBackground); } if (isInCache) { diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java index e490458b38a8..b80fe4871616 100644 --- a/core/java/android/widget/RemoteViewsListAdapter.java +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -85,7 +85,7 @@ public class RemoteViewsListAdapter extends BaseAdapter { public View getView(int position, View convertView, ViewGroup parent) { if (position < getCount()) { RemoteViews rv = mRemoteViewsList.get(position); - rv.setIsWidgetCollectionChild(true); + rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); View v; if (convertView != null && rv != null && convertView.getId() == rv.getLayoutId()) { diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index 2827f634b389..214e5cc01b9e 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -163,7 +163,7 @@ public abstract class RemoteViewsService extends Service { try { rv = mFactory.getViewAt(position); if (rv != null) { - rv.setIsWidgetCollectionChild(true); + rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); } } catch (Exception ex) { Thread t = Thread.currentThread(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 085f8f1d678f..90cf871830fd 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -799,8 +799,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mCursorDrawableRes; + private Drawable mCursorDrawable; // Note: this might be stale if setTextSelectHandleLeft is used. We could simplify the code // by removing it, but we would break apps targeting <= P that use it by reflection. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -3640,6 +3641,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the Drawable corresponding to the right handle used * for selecting text. + * Note that any change applied to the handle Drawable will not be visible + * until the handle is hidden and then drawn again. * * @return the right text selection handle drawable * @@ -3655,6 +3658,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the + * value of the textCursorDrawable attribute. + * Note that any change applied to the cursor Drawable will not be visible + * until the cursor is hidden and then drawn again. + * + * @see #setTextCursorDrawable(int) + * @attr ref android.R.styleable#TextView_textCursorDrawable + */ + public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) { + Preconditions.checkNotNull(textCursorDrawable, + "The cursor drawable should not be null."); + mCursorDrawable = textCursorDrawable; + mCursorDrawableRes = 0; + if (mEditor != null) { + mEditor.loadCursorDrawable(); + } + } + + /** + * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the + * value of the textCursorDrawable attribute. + * Note that any change applied to the cursor Drawable will not be visible + * until the cursor is hidden and then drawn again. + * + * @see #setTextCursorDrawable(Drawable) + * @attr ref android.R.styleable#TextView_textCursorDrawable + */ + public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) { + Preconditions.checkArgumentPositive(textCursorDrawable, + "The cursor drawable should be a valid drawable resource id."); + setTextCursorDrawable(mContext.getDrawable(textCursorDrawable)); + } + + /** + * Returns the Drawable corresponding to the text cursor. + * Note that any change applied to the cursor Drawable will not be visible + * until the cursor is hidden and then drawn again. + * + * @return the text cursor drawable + * + * @see #setTextCursorDrawable(Drawable) + * @see #setTextCursorDrawable(int) + * @attr ref android.R.styleable#TextView_textCursorDrawable + */ + @Nullable public Drawable getTextCursorDrawable() { + if (mCursorDrawable == null && mCursorDrawableRes != 0) { + mCursorDrawable = mContext.getDrawable(mCursorDrawableRes); + } + return mCursorDrawable; + } + + /** * Sets the text appearance from the specified style resource. * <p> * Use a framework-defined {@code TextAppearance} style like diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 875d7c9ee7a6..ff34036ce7e9 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -19,7 +19,9 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Binder; +import android.os.Process; import android.os.SystemClock; +import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; @@ -78,8 +80,10 @@ public class BinderCallsStats implements BinderInternal.Observer { private final Random mRandom; private long mStartTime = System.currentTimeMillis(); private long mCallStatsCount = 0; + private boolean mAddDebugEntries = false; private CachedDeviceState.Readonly mDeviceState; + private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; /** Injector for {@link BinderCallsStats}. */ public static class Injector { @@ -93,7 +97,11 @@ public class BinderCallsStats implements BinderInternal.Observer { } public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { + if (mBatteryStopwatch != null) { + mBatteryStopwatch.close(); + } mDeviceState = deviceState; + mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch(); } @Override @@ -155,8 +163,7 @@ public class BinderCallsStats implements BinderInternal.Observer { return; } - final boolean isWorkSourceSet = workSourceUid >= 0; - final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid); + final UidEntry uidEntry = getUidEntry(workSourceUid); uidEntry.callCount++; if (recordCall) { @@ -317,9 +324,30 @@ public class BinderCallsStats implements BinderInternal.Observer { exported.methodName = methodName; } + // Debug entries added to help validate the data. + if (mAddDebugEntries && mBatteryStopwatch != null) { + resultCallStats.add(createDebugEntry("start_time_millis", mStartTime)); + resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis())); + resultCallStats.add( + createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis())); + } + return resultCallStats; } + private ExportedCallStat createDebugEntry(String variableName, long value) { + final int uid = Process.myUid(); + final ExportedCallStat callStat = new ExportedCallStat(); + callStat.className = ""; + callStat.workSourceUid = uid; + callStat.callingUid = uid; + callStat.recordedCallCount = 1; + callStat.callCount = 1; + callStat.methodName = "__DEBUG_" + variableName; + callStat.latencyMicros = value; + return callStat; + } + /** @hide */ public ArrayMap<String, Integer> getExportedExceptionStats() { synchronized (mLock) { @@ -341,6 +369,8 @@ public class BinderCallsStats implements BinderInternal.Observer { long totalCpuTime = 0; pw.print("Start time: "); pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime)); + pw.print("On battery time (ms): "); + pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0); pw.println("Sampling interval period: " + mPeriodicSamplingInterval); final List<UidEntry> entries = new ArrayList<>(); @@ -434,7 +464,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } protected int getWorkSourceUid() { - return Binder.getCallingWorkSourceUid(); + return ThreadLocalWorkSource.getUid(); } protected long getElapsedRealtimeMicro() { @@ -457,6 +487,10 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + public void setAddDebugEntries(boolean addDebugEntries) { + mAddDebugEntries = addDebugEntries; + } + /** * Sets the maximum number of items to track. */ @@ -496,6 +530,9 @@ public class BinderCallsStats implements BinderInternal.Observer { mUidEntries.clear(); mExceptionCounts.clear(); mStartTime = System.currentTimeMillis(); + if (mBatteryStopwatch != null) { + mBatteryStopwatch.reset(); + } } } diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java index 8c90682ec281..334cca317c30 100644 --- a/core/java/com/android/internal/os/CachedDeviceState.java +++ b/core/java/com/android/internal/os/CachedDeviceState.java @@ -16,8 +16,14 @@ package com.android.internal.os; + +import android.os.SystemClock; + +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; + /** * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with * the System Server telemetry services. @@ -27,6 +33,9 @@ import com.android.internal.annotations.VisibleForTesting; public class CachedDeviceState { private volatile boolean mScreenInteractive; private volatile boolean mCharging; + private final Object mStopwatchesLock = new Object(); + @GuardedBy("mStopwatchLock") + private final ArrayList<TimeInStateStopwatch> mOnBatteryStopwatches = new ArrayList<>(); public CachedDeviceState() { mCharging = true; @@ -44,7 +53,23 @@ public class CachedDeviceState { } public void setCharging(boolean charging) { - mCharging = charging; + if (mCharging != charging) { + mCharging = charging; + updateStopwatches(/* shouldStart= */ !charging); + } + } + + private void updateStopwatches(boolean shouldStart) { + synchronized (mStopwatchesLock) { + final int size = mOnBatteryStopwatches.size(); + for (int i = 0; i < size; i++) { + if (shouldStart) { + mOnBatteryStopwatches.get(i).start(); + } else { + mOnBatteryStopwatches.get(i).stop(); + } + } + } } public Readonly getReadonlyClient() { @@ -62,5 +87,74 @@ public class CachedDeviceState { public boolean isScreenInteractive() { return mScreenInteractive; } + + /** Creates a {@link TimeInStateStopwatch stopwatch} that tracks the time on battery. */ + public TimeInStateStopwatch createTimeOnBatteryStopwatch() { + synchronized (mStopwatchesLock) { + final TimeInStateStopwatch stopwatch = new TimeInStateStopwatch(); + mOnBatteryStopwatches.add(stopwatch); + if (!mCharging) { + stopwatch.start(); + } + return stopwatch; + } + } + } + + /** Tracks the time the device spent in a given state. */ + public class TimeInStateStopwatch implements AutoCloseable { + private final Object mLock = new Object(); + @GuardedBy("mLock") + private long mStartTimeMillis; + @GuardedBy("mLock") + private long mTotalTimeMillis; + + /** Returns the time in state since the last call to {@link TimeInStateStopwatch#reset}. */ + public long getMillis() { + synchronized (mLock) { + return mTotalTimeMillis + elapsedTime(); + } + } + + /** Resets the time in state to 0 without stopping the timer if it's started. */ + public void reset() { + synchronized (mLock) { + mTotalTimeMillis = 0; + mStartTimeMillis = isRunning() ? SystemClock.elapsedRealtime() : 0; + } + } + + private void start() { + synchronized (mLock) { + if (!isRunning()) { + mStartTimeMillis = SystemClock.elapsedRealtime(); + } + } + } + + private void stop() { + synchronized (mLock) { + if (isRunning()) { + mTotalTimeMillis += elapsedTime(); + mStartTimeMillis = 0; + } + } + } + + private long elapsedTime() { + return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMillis : 0; + } + + @VisibleForTesting + public boolean isRunning() { + return mStartTimeMillis > 0; + } + + @Override + public void close() { + synchronized (mStopwatchesLock) { + mOnBatteryStopwatches.remove(this); + } + } } } diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index cf2a297bb6a5..01fd8ba58c74 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -49,6 +49,7 @@ public class LooperStats implements Looper.Observer { private final int mEntriesSizeCap; private int mSamplingInterval; private CachedDeviceState.Readonly mDeviceState; + private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; private long mStartTime = System.currentTimeMillis(); private boolean mAddDebugEntries = false; @@ -58,7 +59,12 @@ public class LooperStats implements Looper.Observer { } public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { + if (mBatteryStopwatch != null) { + mBatteryStopwatch.close(); + } + mDeviceState = deviceState; + mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch(); } public void setAddDebugEntries(boolean addDebugEntries) { @@ -148,9 +154,11 @@ public class LooperStats implements Looper.Observer { maybeAddSpecialEntry(exportedEntries, mOverflowEntry); maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry); // Debug entries added to help validate the data. - if (mAddDebugEntries) { + if (mAddDebugEntries && mBatteryStopwatch != null) { exportedEntries.add(createDebugEntry("start_time_millis", mStartTime)); exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis())); + exportedEntries.add( + createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis())); } return exportedEntries; } @@ -159,7 +167,7 @@ public class LooperStats implements Looper.Observer { final Entry entry = new Entry("__DEBUG_" + variableName); entry.messageCount = 1; entry.recordedMessageCount = 1; - entry.maxDelayMillis = value; + entry.totalLatencyMicro = value; return new ExportedEntry(entry); } @@ -168,6 +176,10 @@ public class LooperStats implements Looper.Observer { return mStartTime; } + public long getBatteryTimeMillis() { + return mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0; + } + private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) { synchronized (specialEntry) { if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) { @@ -188,6 +200,9 @@ public class LooperStats implements Looper.Observer { mOverflowEntry.reset(); } mStartTime = System.currentTimeMillis(); + if (mBatteryStopwatch != null) { + mBatteryStopwatch.reset(); + } } public void setSamplingInterval(int samplingInterval) { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 600b1b324257..53b56f2e937a 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -29,7 +29,7 @@ oneway interface IStatusBar { void setIcon(String slot, in StatusBarIcon icon); void removeIcon(String slot); - void disable(int state1, int state2); + void disable(int displayId, int state1, int state2); void animateExpandNotificationsPanel(); void animateExpandSettingsPanel(String subPanel); void animateCollapsePanels(); @@ -38,8 +38,9 @@ oneway interface IStatusBar void showWirelessChargingAnimation(int batteryLevel); /** - * Notifies the status bar of a System UI visibility flag change. + * Notifies System UI side of a visibility flag change on the specified display. * + * @param displayId the id of the display to notify * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported * separately in fullscreenStackVis and dockedStackVis * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack, @@ -50,13 +51,13 @@ oneway interface IStatusBar * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates * @param dockedBounds the current bounds of the docked stack, in screen coordinates */ - void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, - in Rect fullscreenBounds, in Rect dockedBounds); + void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, + int mask, in Rect fullscreenBounds, in Rect dockedBounds); - void topAppWindowChanged(boolean menuVisible); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition, + void topAppWindowChanged(int displayId, boolean menuVisible); + void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition, boolean showImeSwitcher); - void setWindowState(int window, int state); + void setWindowState(int display, int window, int state); void showRecentApps(boolean triggeredFromAltTab); void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); @@ -70,30 +71,38 @@ oneway interface IStatusBar void toggleKeyboardShortcutsMenu(int deviceId); /** - * Notifies the status bar that an app transition is pending to delay applying some flags with - * visual impact until {@link #appTransitionReady} is called. + * Notifies System UI on the specified display that an app transition is pending to delay + * applying some flags with visual impact until {@link #appTransitionReady} is called. + * + * @param displayId the id of the display to notify */ - void appTransitionPending(); + void appTransitionPending(int displayId); /** - * Notifies the status bar that a pending app transition has been cancelled. + * Notifies System UI on the specified display that a pending app transition has been cancelled. + * + * @param displayId the id of the display to notify */ - void appTransitionCancelled(); + void appTransitionCancelled(int displayId); /** - * Notifies the status bar that an app transition is now being executed. + * Notifies System UI on the specified display that an app transition is now being executed. * + * @param displayId the id of the display to notify * @param statusBarAnimationsStartTime the desired start time for all visual animations in the * status bar caused by this app transition in uptime millis * @param statusBarAnimationsDuration the duration for all visual animations in the status * bar caused by this app transition in millis */ - void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration); + void appTransitionStarting(int displayId, long statusBarAnimationsStartTime, + long statusBarAnimationsDuration); /** - * Notifies the status bar that an app transition is done. + * Notifies System UI on the specified display that an app transition is done. + * + * @param displayId the id of the display to notify */ - void appTransitionFinished(); + void appTransitionFinished(int displayId); void showAssistDisclosure(); void startAssist(in Bundle args); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index bf82dc610ad4..5118e5f0473e 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -41,6 +41,7 @@ interface IStatusBarService void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription); void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); + // TODO(b/117478341): support back button change when IME is showing on a external display. void setImeWindowStatus(in IBinder token, int vis, int backDisposition, boolean showImeSwitcher); void expandSettingsPanel(String subPanel); @@ -69,7 +70,7 @@ interface IStatusBarService void onNotificationSmartRepliesAdded(in String key, in int replyCount); void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant); void onNotificationSettingsViewed(String key); - void setSystemUiVisibility(int vis, int mask, String cause); + void setSystemUiVisibility(int displayId, int vis, int mask, String cause); void onGlobalActionsShown(); void onGlobalActionsHidden(); diff --git a/core/java/com/android/internal/widget/ImageMessageConsumer.java b/core/java/com/android/internal/widget/ImageMessageConsumer.java new file mode 100644 index 000000000000..01613dcdf3fa --- /dev/null +++ b/core/java/com/android/internal/widget/ImageMessageConsumer.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** + * An interface for the class who will use the {@link ImageResolver} to resolve images. + */ +public interface ImageMessageConsumer { + /** + * Set the custom {@link ImageResolver} other than {@link LocalImageResolver}. + * @param resolver An image resolver that has custom implementation. + */ + void setImageResolver(ImageResolver resolver); +} diff --git a/core/java/com/android/internal/widget/ImageResolver.java b/core/java/com/android/internal/widget/ImageResolver.java new file mode 100644 index 000000000000..45885257ad8d --- /dev/null +++ b/core/java/com/android/internal/widget/ImageResolver.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.graphics.drawable.Drawable; +import android.net.Uri; + +/** + * An interface for image resolvers that have custom implementations like cache mechanisms. + */ +public interface ImageResolver { + /** + * Load an image from specified uri. + * @param uri Uri of the target image. + * @return Target image in Drawable. + */ + Drawable loadImage(Uri uri); +} diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index 71d3bb5d6b5c..2302de2cd058 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -17,7 +17,6 @@ package com.android.internal.widget; import android.annotation.Nullable; -import android.app.Notification; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index 607a3a9ab542..64650a7ebc2f 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -25,6 +25,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.util.Pools; @@ -57,6 +58,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage private int mActualWidth; private int mActualHeight; private boolean mIsIsolated; + private ImageResolver mImageResolver; public MessagingImageMessage(@NonNull Context context) { this(context, null); @@ -96,11 +98,16 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage MessagingMessage.super.setMessage(message); Drawable drawable; try { - drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext()); + Uri uri = message.getDataUri(); + drawable = mImageResolver != null ? mImageResolver.loadImage(uri) : + LocalImageResolver.resolveImage(uri, getContext()); } catch (IOException | SecurityException e) { e.printStackTrace(); return false; } + if (drawable == null) { + return false; + } int intrinsicHeight = drawable.getIntrinsicHeight(); if (intrinsicHeight == 0) { Log.w(TAG, "Drawable with 0 intrinsic height was returned"); @@ -114,7 +121,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage } static MessagingMessage createMessage(MessagingLayout layout, - Notification.MessagingStyle.Message m) { + Notification.MessagingStyle.Message m, ImageResolver resolver) { MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); MessagingImageMessage createdMessage = sInstancePool.acquire(); if (createdMessage == null) { @@ -125,6 +132,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage false); createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); } + createdMessage.setImageResolver(resolver); boolean created = createdMessage.setMessage(m); if (!created) { createdMessage.recycle(); @@ -133,6 +141,10 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage return createdMessage; } + private void setImageResolver(ImageResolver resolver) { + mImageResolver = resolver; + } + @Override protected void onDraw(Canvas canvas) { canvas.save(); diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 0f2e9c52add0..07d0d7d91997 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -57,7 +57,7 @@ import java.util.regex.Pattern; * messages and adapts the layout accordingly. */ @RemoteViews.RemoteView -public class MessagingLayout extends FrameLayout { +public class MessagingLayout extends FrameLayout implements ImageMessageConsumer { private static final float COLOR_SHIFT_AMOUNT = 60; /** @@ -95,6 +95,7 @@ public class MessagingLayout extends FrameLayout { private Person mUser; private CharSequence mNameReplacement; private boolean mDisplayImagesAtEnd; + private ImageResolver mImageResolver; public MessagingLayout(@NonNull Context context) { super(context); @@ -167,6 +168,11 @@ public class MessagingLayout extends FrameLayout { bind(newMessages, newHistoricMessages, showSpinner); } + @Override + public void setImageResolver(ImageResolver resolver) { + mImageResolver = resolver; + } + private void addRemoteInputHistoryToMessages( List<Notification.MessagingStyle.Message> newMessages, CharSequence[] remoteInputHistory) { @@ -463,12 +469,12 @@ public class MessagingLayout extends FrameLayout { */ private List<MessagingMessage> createMessages( List<Notification.MessagingStyle.Message> newMessages, boolean historic) { - List<MessagingMessage> result = new ArrayList<>();; + List<MessagingMessage> result = new ArrayList<>(); for (int i = 0; i < newMessages.size(); i++) { Notification.MessagingStyle.Message m = newMessages.get(i); MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { - message = MessagingMessage.createMessage(this, m); + message = MessagingMessage.createMessage(this, m, mImageResolver); } message.setIsHistoric(historic); result.add(message); diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java index 74d0aae3634b..c32d3705bba7 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -33,9 +33,9 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { String IMAGE_MIME_TYPE_PREFIX = "image/"; static MessagingMessage createMessage(MessagingLayout layout, - Notification.MessagingStyle.Message m) { + Notification.MessagingStyle.Message m, ImageResolver resolver) { if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) { - return MessagingImageMessage.createMessage(layout, m); + return MessagingImageMessage.createMessage(layout, m, resolver); } else { return MessagingTextMessage.createMessage(layout, m); } diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp index dcb787462a13..cfe742d0134d 100644 --- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp +++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp @@ -59,7 +59,7 @@ struct HarfBuzzFontData { static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) { ALOG_ASSERT(codepoint <= 0xFFFF); - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint->setTextEncoding(kGlyphID_SkTextEncoding); SkScalar skWidth; SkRect skBounds; @@ -84,7 +84,7 @@ static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoin { HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData); SkPaint* paint = hbFontData->m_paint; - paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); + paint->setTextEncoding(kUTF32_SkTextEncoding); if (unicode > 0x10ffff) { unicode = 0xfffd; diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index a8b0640c3a73..c249e209b29f 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -71,7 +71,7 @@ static JMetricsID gFontMetricsInt_fieldID; static void defaultSettingsForAndroid(Paint* paint) { // GlyphID encoding is required because we are using Harfbuzz shaping - paint->setTextEncoding(Paint::kGlyphID_TextEncoding); + paint->setTextEncoding(kGlyphID_SkTextEncoding); } namespace PaintGlue { @@ -321,7 +321,7 @@ namespace PaintGlue { x += MinikinUtils::xOffsetForTextAlign(paint, layout); Paint::Align align = paint->getTextAlign(); paint->setTextAlign(Paint::kLeft_Align); - paint->setTextEncoding(Paint::kGlyphID_TextEncoding); + paint->setTextEncoding(kGlyphID_SkTextEncoding); GetTextFunctor f(layout, path, x, y, paint, glyphs, pos); MinikinUtils::forFontRun(layout, paint, f); paint->setTextAlign(align); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 8e9a0bf61517..02867304af36 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -102,6 +102,11 @@ enum MountExternalKind { MOUNT_EXTERNAL_FULL = 4, }; +// Must match values in com.android.internal.os.Zygote. +enum RuntimeFlags : uint32_t { + DEBUG_ENABLE_JDWP = 1, +}; + static void RuntimeAbort(JNIEnv* env, int line, const char* msg) { std::ostringstream oss; oss << __FILE__ << ":" << line << ": " << msg; @@ -254,6 +259,36 @@ static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error return true; } +static void EnableDebugger() { + // To let a non-privileged gdbserver attach to this + // process, we must set our dumpable flag. + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { + ALOGE("prctl(PR_SET_DUMPABLE) failed"); + } + + // A non-privileged native debugger should be able to attach to the debuggable app, even if Yama + // is enabled (see kernel/Documentation/security/Yama.txt). + if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == -1) { + // if Yama is off prctl(PR_SET_PTRACER) returns EINVAL - don't log in this + // case since it's expected behaviour. + if (errno != EINVAL) { + ALOGE("prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) failed"); + } + } + + // We don't want core dumps, though, so set the soft limit on core dump size + // to 0 without changing the hard limit. + rlimit rl; + if (getrlimit(RLIMIT_CORE, &rl) == -1) { + ALOGE("getrlimit(RLIMIT_CORE) failed"); + } else { + rl.rlim_cur = 0; + if (setrlimit(RLIMIT_CORE, &rl) == -1) { + ALOGE("setrlimit(RLIMIT_CORE) failed"); + } + } +} + // The debug malloc library needs to know whether it's the zygote or a child. extern "C" int gMallocLeakZygoteChild; @@ -956,6 +991,11 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } } + // Set process properties to enable debugging if required. + if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) { + EnableDebugger(); + } + if (NeedsNoRandomizeWorkaround()) { // Work around ARM kernel ASLR lossage (http://b/5817320). int old_personality = personality(0xffffffff); diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 3a908dc28e4c..8e4eb0018999 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -38,6 +38,12 @@ enum PageId { // Unknown page. Should not be used in production code. PAGE_UNKNOWN = 0; + // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device) + BLUETOOTH_DEVICE_DETAILS = 1009; + + // OPEN: Settings > Connected devices > Bluetooth > Pair new device + BLUETOOTH_PAIRING = 1018; + // OPEN: Settings homepage SETTINGS_HOMEPAGE = 1502; diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index d917536b1715..4c1fc5c89b0d 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -761,6 +761,13 @@ message GlobalSettingsProto { } optional SmartSelection smart_selection = 108; + message SmartSuggestions { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto service_explicitly_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional SmartSuggestions smart_suggestions = 145; + message Sms { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -869,6 +876,8 @@ message GlobalSettingsProto { // Temperature at which the high temperature warning notification should // be shown. optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // USB temperature at which the high temperature alarm notification should be shown. + optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional TemperatureWarning temperature_warning = 119; @@ -991,5 +1000,5 @@ message GlobalSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 145; + // Next tag = 146; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index ab50ad147107..60561bd9dbc5 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -185,6 +185,7 @@ message ProcessRecordProto { optional int32 app_id = 5; optional int32 isolated_app_id = 6; optional bool persistent = 7; + optional int32 lru_index = 8; } message BroadcastRecordProto { diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index e83a2bfac77a..231caabe0335 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -215,6 +215,34 @@ message ConstantsProto { // The fraction of a prefetch job's running window that must pass before // we consider matching it against a metered network. optional double conn_prefetch_relax_frac = 22; + // Whether to use heartbeats or rolling window for quota management. True + // will use heartbeats, false will use a rolling window. + optional bool use_heartbeats = 23; + + message QuotaController { + // How much time each app will have to run jobs within their standby bucket window. + optional int64 allowed_time_per_period_ms = 1; + // How much time the package should have before transitioning from out-of-quota to in-quota. + // This should not affect processing if the package is already in-quota. + optional int64 in_quota_buffer_ms = 2; + // The quota window size of the particular standby bucket. Apps in this standby bucket are + // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + // WINDOW_SIZE_MS. + optional int64 active_window_size_ms = 3; + // The quota window size of the particular standby bucket. Apps in this standby bucket are + // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + // WINDOW_SIZE_MS. + optional int64 working_window_size_ms = 4; + // The quota window size of the particular standby bucket. Apps in this standby bucket are + // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + // WINDOW_SIZE_MS. + optional int64 frequent_window_size_ms = 5; + // The quota window size of the particular standby bucket. Apps in this standby bucket are + // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + // WINDOW_SIZE_MS. + optional int64 rare_window_size_ms = 6; + } + optional QuotaController quota_controller = 24; } message StateControllerProto { @@ -357,6 +385,65 @@ message StateControllerProto { } repeated TrackedJob tracked_jobs = 2; } + message QuotaController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool is_charging = 1; + optional bool is_in_parole = 2; + + message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional JobStatusShortInfoProto info = 1; + optional int32 source_uid = 2; + optional JobStatusDumpProto.Bucket effective_standby_bucket = 3; + optional bool has_quota = 4; + // The amount of time that this job has remaining in its quota. This + // can be negative if the job is out of quota. + optional int64 remaining_quota_ms = 5; + } + repeated TrackedJob tracked_jobs = 3; + + message Package { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 user_id = 1; + optional string name = 2; + } + + message TimingSession { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int64 start_time_elapsed = 1; + optional int64 end_time_elapsed = 2; + optional int32 job_count = 3; + } + + message Timer { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional Package pkg = 1; + // True if the Timer is actively tracking jobs. + optional bool is_active = 2; + // The time this timer last became active. Only valid if is_active is true. + optional int64 start_time_elapsed = 3; + // How many are currently running. Valid only if the device is_active is true. + optional int32 job_count = 4; + // All of the jobs that the Timer is currently tracking. + repeated JobStatusShortInfoProto running_jobs = 5; + } + + message PackageStats { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional Package pkg = 1; + + optional Timer timer = 2; + + repeated TimingSession saved_sessions = 3; + } + repeated PackageStats package_stats = 4; + } message StorageController { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -403,8 +490,10 @@ message StateControllerProto { ContentObserverController content_observer = 4; DeviceIdleJobsController device_idle = 5; IdleController idle = 6; + QuotaController quota = 9; StorageController storage = 7; TimeController time = 8; + // Next tag: 10 } } @@ -603,11 +692,13 @@ message JobStatusDumpProto { CONSTRAINT_CONNECTIVITY = 7; CONSTRAINT_CONTENT_TRIGGER = 8; CONSTRAINT_DEVICE_NOT_DOZING = 9; + CONSTRAINT_WITHIN_QUOTA = 10; } repeated Constraint required_constraints = 7; repeated Constraint satisfied_constraints = 8; repeated Constraint unsatisfied_constraints = 9; optional bool is_doze_whitelisted = 10; + optional bool is_uid_active = 26; message ImplicitConstraints { // The device isn't Dozing or this job will be in the foreground. This @@ -627,6 +718,7 @@ message JobStatusDumpProto { TRACKING_IDLE = 3; TRACKING_STORAGE = 4; TRACKING_TIME = 5; + TRACKING_QUOTA = 6; } // Controllers that are currently tracking the job. repeated TrackingController tracking_controllers = 11; @@ -660,6 +752,7 @@ message JobStatusDumpProto { NEVER = 4; } optional Bucket standby_bucket = 17; + optional bool is_exempted_from_app_standby = 27; optional int64 enqueue_duration_ms = 18; // Can be negative if the earliest runtime deadline has passed. @@ -674,5 +767,5 @@ message JobStatusDumpProto { optional int64 internal_flags = 24; - // Next tag: 26 + // Next tag: 28 } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index df17b4c453d9..3018614fee61 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3387,6 +3387,13 @@ <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to control display color transformations. + <p>Not for use by third-party applications.</p> + @hide + @SystemApi --> + <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" + android:protectionLevel="signature|privileged" /> + <!-- Allows an application to collect usage infomation about brightness slider changes. <p>Not for use by third-party applications.</p> @hide diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml index 71632a206e3c..2b15dceeb469 100644 --- a/core/res/res/layout/notification_material_reply_text.xml +++ b/core/res/res/layout/notification_material_reply_text.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="@dimen/notification_content_margin_end" android:visibility="gone" - android:textAppearance="@style/TextAppearance.Material.Notification.Reply" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" android:singleLine="true" /> <TextView @@ -48,7 +48,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="@dimen/notification_content_margin_end" android:visibility="gone" - android:textAppearance="@style/TextAppearance.Material.Notification.Reply" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" android:singleLine="true" /> <LinearLayout @@ -64,7 +64,7 @@ android:layout_weight="1" android:layout_marginEnd="@dimen/notification_content_margin_end" android:layout_gravity="center" - android:textAppearance="@style/TextAppearance.Material.Notification.Reply" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" android:singleLine="true" /> <ProgressBar android:id="@+id/notification_material_reply_progress" diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml index c00acd5bfc60..be5d9b436b79 100644 --- a/core/res/res/layout/notification_template_ambient_header.xml +++ b/core/res/res/layout/notification_template_ambient_header.xml @@ -24,5 +24,5 @@ android:layout_height="wrap_content"> <include layout="@layout/notification_template_header" - android:theme="@style/Theme.Material.Notification.Ambient"/> + android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/> </FrameLayout> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 4bf1ad6651ee..5ba1cf259551 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -17,7 +17,7 @@ <!-- extends ViewGroup --> <NotificationHeaderView xmlns:android="http://schemas.android.com/apk/res/android" - android:theme="@style/Theme.Material.Notification" + android:theme="@style/Theme.DeviceDefault.Notification" android:id="@+id/notification_header" android:orientation="horizontal" android:layout_width="wrap_content" diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml index c8864c2f829d..2c6064ea6243 100644 --- a/core/res/res/layout/notification_template_material_ambient.xml +++ b/core/res/res/layout/notification_template_material_ambient.xml @@ -24,7 +24,7 @@ android:paddingEnd="@dimen/notification_extra_margin_ambient" > <include layout="@layout/notification_template_ambient_header" - android:theme="@style/Theme.Material.Notification.Ambient" /> + android:theme="@style/Theme.DeviceDefault.Notification.Ambient" /> <LinearLayout android:id="@+id/notification_action_list_margin_target" @@ -51,8 +51,7 @@ android:orientation="vertical" > <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.Material.Notification.Title" - android:fontFamily="sans-serif" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="top|center_horizontal" @@ -65,7 +64,7 @@ <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="0dp" - android:textAppearance="@style/TextAppearance.Material.Notification" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification" android:singleLine="false" android:layout_weight="1" android:gravity="top|center_horizontal" diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 26eb4e7b2c6c..d5ea96f6bc16 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -53,7 +53,7 @@ android:layout_marginTop="@dimen/notification_progress_margin_top" android:layout_marginBottom="6dp"/> <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/notification_text_margin_top" diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml index d6f0e1df430e..eb8925819cd3 100644 --- a/core/res/res/layout/notification_template_material_inbox.xml +++ b/core/res/res/layout/notification_template_material_inbox.xml @@ -53,7 +53,7 @@ android:layout_marginTop="@dimen/notification_progress_margin_top" android:layout_marginBottom="2dp"/> <TextView android:id="@+id/inbox_text0" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -62,7 +62,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text1" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -71,7 +71,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text2" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -80,7 +80,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text3" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -89,7 +89,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text4" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -98,7 +98,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text5" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" @@ -107,7 +107,7 @@ android:layout_weight="1" /> <TextView android:id="@+id/inbox_text6" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="0dp" android:singleLine="true" diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml index 0717d962d69f..483b479538a1 100644 --- a/core/res/res/layout/notification_template_messaging_group.xml +++ b/core/res/res/layout/notification_template_messaging_group.xml @@ -34,7 +34,7 @@ android:orientation="vertical"> <com.android.internal.widget.ImageFloatingTextView android:id="@+id/message_name" - style="@style/Widget.Material.Notification.MessagingName" + style="@style/Widget.DeviceDefault.Notification.MessagingName" android:layout_width="wrap_content" android:textAlignment="viewStart" /> diff --git a/core/res/res/layout/notification_template_messaging_text_message.xml b/core/res/res/layout/notification_template_messaging_text_message.xml index 3611186e279b..26c081e1f3d7 100644 --- a/core/res/res/layout/notification_template_messaging_text_message.xml +++ b/core/res/res/layout/notification_template_messaging_text_message.xml @@ -18,5 +18,5 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/message_text" android:textAlignment="viewStart" - style="@style/Widget.Material.Notification.MessagingText" + style="@style/Widget.DeviceDefault.Notification.MessagingText" /> diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml index 6459bb8019b4..622f080653a4 100644 --- a/core/res/res/layout/notification_template_part_line1.xml +++ b/core/res/res/layout/notification_template_part_line1.xml @@ -22,7 +22,7 @@ android:orientation="horizontal" > <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.Material.Notification.Title" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" @@ -31,7 +31,7 @@ android:textAlignment="viewStart" /> <TextView android:id="@+id/text_line_1" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="end|bottom" diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml index 3b27666ef6ff..01b14ae0592d 100644 --- a/core/res/res/layout/notification_template_text.xml +++ b/core/res/res/layout/notification_template_text.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> <com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/Widget.Material.Notification.Text" + style="@style/Widget.DeviceDefault.Notification.Text" android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0cd6bc5cd700..c62071bdf552 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3350,6 +3350,14 @@ --> <string name="config_defaultTextClassifierPackage" translatable="false"></string> + <!-- The package name for the system's smart suggestion service. + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, content capture and + smart suggestions will be disabled. + Example: "com.android.intelligence/.SmartSuggestionsService" + --> + <string name="config_defaultSmartSuggestionsService" translatable="false"></string> + <!-- Whether the device uses the default focus highlight when focus state isn't specified. --> <bool name="config_useDefaultFocusHighlight">true</bool> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index d722961981c5..c03d570001d6 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -113,6 +113,15 @@ easier. <style name="Widget.DeviceDefault.ListView.White" parent="Widget.Material.ListView.White"/> <style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Material.MediaRouteButton" /> <style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Material.NumberPicker"/> + <style name="Widget.DeviceDefault.Notification.Text" parent="Widget.Material.Notification.Text"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item> + </style> + <style name="Widget.DeviceDefault.Notification.MessagingText" parent="Widget.Material.Notification.MessagingText"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item> + </style> + <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item> + </style> <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/> <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/> <style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/> @@ -250,6 +259,16 @@ easier. <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title"> <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> </style> + + <!-- Notification Styles --> + <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification"> + <item name="fontFamily">@string/config_headlineFontFamily</item> + </style> + <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title"> + <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> + </style> + <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply" /> + <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/> <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/> <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 67b3c92e69db..5a7199d6f445 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -1313,4 +1313,5 @@ please see styles_device_defaults.xml. <item name="gravity">top|center_horizontal</item> </style> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 01422c8ffd97..6854a84e950a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3265,6 +3265,7 @@ <java-symbol type="string" name="notification_channel_do_not_disturb" /> <java-symbol type="string" name="config_defaultAutofillService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> + <java-symbol type="string" name="config_defaultSmartSuggestionsService" /> <java-symbol type="string" name="notification_channel_foreground_service" /> <java-symbol type="string" name="foreground_service_app_in_background" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index fa009bd60c72..fec101a6fff3 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1724,4 +1724,10 @@ easier. <item name="layout_gravity">center</item> </style> + <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification"> + </style> + + <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient"> + </style> + </resources> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index a8f9e8a5891b..ed9c3d5b2b70 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -415,6 +415,7 @@ public class SettingsBackupTest { Settings.Global.SHOW_TEMPERATURE_WARNING, Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL, Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL, + Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED, Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS, Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT, @@ -477,6 +478,7 @@ public class SettingsBackupTest { Settings.Global.USER_SWITCHER_ENABLED, Settings.Global.NETWORK_ACCESS_TIMEOUT_MS, Settings.Global.WARNING_TEMPERATURE, + Settings.Global.USB_ALARM_TEMPERATURE, Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY, Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, Settings.Global.WEBVIEW_MULTIPROCESS, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index fbcb629f043f..aec4571252e7 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -373,7 +373,7 @@ public class TextClassifierTest { public void testSuggestConversationActions_textReplyOnly_maxThree() { if (isTextClassifierDisabled()) return; ConversationActions.Message message = - new ConversationActions.Message.Builder().setText("Hello").build(); + new ConversationActions.Message.Builder().setText("Where are you?").build(); ConversationActions.TypeConfig typeConfig = new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false) .setIncludedTypes( diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 8691e73f82fb..e2618191235d 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -17,6 +17,7 @@ package com.android.internal.os; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.os.Binder; import android.platform.test.annotations.Presubmit; @@ -387,8 +388,7 @@ public class BinderCallsStatsTest { @Test public void testNoDataCollectedBeforeInitialDeviceStateSet() { - TestBinderCallsStats bcs = new TestBinderCallsStats(); - bcs.setDeviceState(null); + TestBinderCallsStats bcs = new TestBinderCallsStats(null); bcs.setDetailedTracking(true); Binder binder = new Binder(); CallSession callSession = bcs.callStarted(binder, 1); @@ -528,22 +528,6 @@ public class BinderCallsStatsTest { } @Test - public void testCallingUidUsedWhenWorkSourceNotSet() { - TestBinderCallsStats bcs = new TestBinderCallsStats(); - bcs.setDetailedTracking(true); - bcs.workSourceUid = -1; - - Binder binder = new Binder(); - CallSession callSession = bcs.callStarted(binder, 1); - bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); - - assertEquals(1, bcs.getExportedCallStats().size()); - BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0); - assertEquals(CALLING_UID, stat.workSourceUid); - assertEquals(CALLING_UID, stat.callingUid); - } - - @Test public void testGetExportedStatsWithoutCalls() { TestBinderCallsStats bcs = new TestBinderCallsStats(); Binder binder = new Binder(); @@ -613,6 +597,27 @@ public class BinderCallsStatsTest { assertEquals(CALLING_UID, callStats.callingUid); } + @Test + public void testAddsDebugEntries() { + long startTime = System.currentTimeMillis(); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setAddDebugEntries(true); + ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats(); + assertEquals(3, callStats.size()); + BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0); + assertEquals("", debugEntry1.className); + assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName); + assertTrue(startTime <= debugEntry1.latencyMicros); + BinderCallsStats.ExportedCallStat debugEntry2 = callStats.get(1); + assertEquals("", debugEntry2.className); + assertEquals("__DEBUG_end_time_millis", debugEntry2.methodName); + assertTrue(debugEntry1.latencyMicros <= debugEntry2.latencyMicros); + BinderCallsStats.ExportedCallStat debugEntry3 = callStats.get(2); + assertEquals("", debugEntry3.className); + assertEquals("__DEBUG_battery_time_millis", debugEntry3.methodName); + assertTrue(debugEntry3.latencyMicros >= 0); + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public int workSourceUid = WORKSOURCE_UID; @@ -620,6 +625,10 @@ public class BinderCallsStatsTest { public long elapsedTime = 0; TestBinderCallsStats() { + this(mDeviceState); + } + + TestBinderCallsStats(CachedDeviceState deviceState) { // Make random generator not random. super(new Injector() { public Random getRandomGenerator() { @@ -633,7 +642,10 @@ public class BinderCallsStatsTest { } }); setSamplingInterval(1); - setDeviceState(mDeviceState.getReadonlyClient()); + setAddDebugEntries(false); + if (deviceState != null) { + setDeviceState(deviceState.getReadonlyClient()); + } } @Override diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 31dde5c79f27..f26dfad0b037 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -273,8 +273,7 @@ public final class LooperStatsTest { @Test public void testDataNotCollectedBeforeDeviceStateSet() { - TestableLooperStats looperStats = new TestableLooperStats(1, 100); - looperStats.setDeviceState(null); + TestableLooperStats looperStats = new TestableLooperStats(1, 100, null); Object token1 = looperStats.messageDispatchStarting(); looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); @@ -439,15 +438,19 @@ public final class LooperStatsTest { looperStats.messageDispatched(token, message); List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); - assertThat(entries).hasSize(3); + assertThat(entries).hasSize(4); LooperStats.ExportedEntry debugEntry1 = entries.get(1); assertThat(debugEntry1.handlerClassName).isEqualTo(""); assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis"); - assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis()); + assertThat(debugEntry1.totalLatencyMicros).isEqualTo(looperStats.getStartTimeMillis()); LooperStats.ExportedEntry debugEntry2 = entries.get(2); assertThat(debugEntry2.handlerClassName).isEqualTo(""); assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis"); - assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis()); + assertThat(debugEntry2.totalLatencyMicros).isAtLeast(looperStats.getStartTimeMillis()); + LooperStats.ExportedEntry debugEntry3 = entries.get(3); + assertThat(debugEntry3.handlerClassName).isEqualTo(""); + assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis"); + assertThat(debugEntry3.totalLatencyMicros).isAtLeast(0L); } private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { @@ -468,10 +471,16 @@ public final class LooperStatsTest { private int mSamplingInterval; TestableLooperStats(int samplingInterval, int sizeCap) { + this(samplingInterval, sizeCap, mDeviceState); + } + + TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) { super(samplingInterval, sizeCap); - this.mSamplingInterval = samplingInterval; - this.setDeviceState(mDeviceState.getReadonlyClient()); - this.setAddDebugEntries(false); + mSamplingInterval = samplingInterval; + setAddDebugEntries(false); + if (deviceState != null) { + setDeviceState(deviceState.getReadonlyClient()); + } } void tickRealtime(long micros) { diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 9e4ea32ed605..f2373446924a 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -281,6 +281,7 @@ applications that come with the platform <permission name="android.permission.WRITE_APN_SETTINGS"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> </privapp-permissions> <privapp-permissions package="com.android.settings.intelligence"> @@ -408,6 +409,7 @@ applications that come with the platform <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> </privapp-permissions> <privapp-permissions package="com.android.tv"> diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java index dea2f45e9d33..cb12a7c6ba6b 100644 --- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java @@ -19,6 +19,7 @@ package android.graphics.drawable; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.app.ActivityThread; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -150,11 +151,14 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) { mLayerState = createConstantState(state, res); - - if (sMask == null) { - sMask = PathParser.createPathFromPathData( - Resources.getSystem().getString(R.string.config_icon_mask)); - } + // config_icon_mask from context bound resource may have been chaged using + // OverlayManager. Read that one first. + Resources r = ActivityThread.currentActivityThread() == null + ? Resources.getSystem() + : ActivityThread.currentActivityThread().getApplication().getResources(); + // TODO: either make sMask update only when config_icon_mask changes OR + // get rid of it all-together in layoutlib + sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask)); mMask = new Path(sMask); mMaskScaleOnly = new Path(mMask); mMaskMatrix = new Matrix(); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 68d216d286cf..c20c720eadbb 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -583,7 +583,65 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_package_map_.emplace_back(std::move(package_name), dtohl(entry_iter->packageId)); } + } break; + + case RES_TABLE_OVERLAYABLE_TYPE: { + const ResTable_overlayable_header* header = + child_chunk.header<ResTable_overlayable_header>(); + if (header == nullptr) { + LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small."; + return {}; + } + + // Iterate over the overlayable policy chunks + ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size()); + while (overlayable_iter.HasNext()) { + const Chunk overlayable_child_chunk = overlayable_iter.Next(); + + switch (overlayable_child_chunk.type()) { + case RES_TABLE_OVERLAYABLE_POLICY_TYPE: { + const ResTable_overlayable_policy_header* policy_header = + overlayable_child_chunk.header<ResTable_overlayable_policy_header>(); + if (policy_header == nullptr) { + LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small."; + return {}; + } + + if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref)) + < dtohl(policy_header->entry_count)) { + LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries."; + return {}; + } + + // Retrieve all the ids belonging to this policy + std::unordered_set<uint32_t> ids; + const auto ids_begin = + reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr()); + const auto ids_end = ids_begin + dtohl(policy_header->entry_count); + for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) { + ids.insert(dtohl(id_iter->ident)); + } + + // Add the pairing of overlayable properties to resource ids to the package + OverlayableInfo overlayable_info; + overlayable_info.policy_flags = policy_header->policy_flags; + loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); + break; + } + + default: + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + break; + } + } + if (overlayable_iter.HadError()) { + LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s", + overlayable_iter.GetLastError().c_str()); + if (overlayable_iter.HadFatalError()) { + return {}; + } + } } break; default: diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 2fe98b00f3a2..63b25270f116 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -7076,7 +7076,7 @@ public: } } - const auto& getTypeMapping() const { + const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const { return mTypeMapping->mData; } @@ -7137,9 +7137,6 @@ status_t ResTable::createIdmap(const ResTable& targetResTable, const PackageGroup* packageGroup = mPackageGroups[0]; - // the number of resources overlaid that were not explicitly marked overlayable - size_t forcedOverlayCount = 0u; - // find the resources that exist in both packages auto typeMapping = std::make_unique<IdmapTypeMapping>(); for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) { @@ -7170,11 +7167,6 @@ status_t ResTable::createIdmap(const ResTable& targetResTable, continue; } - if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) & - ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) { - ++forcedOverlayCount; - } - typeMapping->add(target_resid, overlay_resid); } } @@ -7243,10 +7235,6 @@ status_t ResTable::createIdmap(const ResTable& targetResTable, typeData += entryCount * 2; } - if (forcedOverlayCount > 0) { - ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount); - } - return NO_ERROR; } diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h index 99a52dc9244e..a0f23433c676 100644 --- a/libs/androidfw/include/androidfw/Chunk.h +++ b/libs/androidfw/include/androidfw/Chunk.h @@ -89,7 +89,9 @@ class ChunkIterator { len_(len), last_error_(nullptr) { CHECK(next_chunk_ != nullptr) << "data can't be nullptr"; - VerifyNextChunk(); + if (len_ != 0) { + VerifyNextChunk(); + } } Chunk Next(); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 349b379778a6..8c5c3b7d3858 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -20,6 +20,7 @@ #include <memory> #include <set> #include <vector> +#include <unordered_set> #include "android-base/macros.h" @@ -76,6 +77,10 @@ struct TypeSpec { // TypeSpecPtr is a managed pointer that knows how to delete itself. using TypeSpecPtr = util::unique_cptr<TypeSpec>; +struct OverlayableInfo { + uint32_t policy_flags; +}; + class LoadedPackage { public: class iterator { @@ -216,6 +221,18 @@ class LoadedPackage { } } + // Retrieve the overlayable properties of the specified resource. If the resource is not + // overlayable, this will return a null pointer. + const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const { + for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids + : overlayable_infos_) { + if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) { + return &overlayable_info_ids.first; + } + } + return nullptr; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); @@ -233,6 +250,7 @@ class LoadedPackage { ByteBucketArray<TypeSpecPtr> type_specs_; ByteBucketArray<uint32_t> resource_ids_; std::vector<DynamicPackageEntry> dynamic_package_map_; + std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_; }; // Read-only view into a resource table. This class validates all data diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index ad33fcfa2429..91261aa3e4f9 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -234,7 +234,9 @@ enum { RES_TABLE_PACKAGE_TYPE = 0x0200, RES_TABLE_TYPE_TYPE = 0x0201, RES_TABLE_TYPE_SPEC_TYPE = 0x0202, - RES_TABLE_LIBRARY_TYPE = 0x0203 + RES_TABLE_LIBRARY_TYPE = 0x0203, + RES_TABLE_OVERLAYABLE_TYPE = 0x0204, + RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205, }; /** @@ -1354,10 +1356,6 @@ struct ResTable_typeSpec enum : uint32_t { // Additional flag indicating an entry is public. SPEC_PUBLIC = 0x40000000u, - - // Additional flag indicating an entry is overlayable at runtime. - // Added in Android-P. - SPEC_OVERLAYABLE = 0x80000000u, }; }; @@ -1607,6 +1605,49 @@ struct ResTable_lib_entry uint16_t packageName[128]; }; +/** + * Specifies the set of resources that are explicitly allowed to be overlaid by RROs. + */ +struct ResTable_overlayable_header +{ + struct ResChunk_header header; +}; + +/** + * Holds a list of resource ids that are protected from being overlaid by a set of policies. If + * the overlay fulfils at least one of the policies, then the overlay can overlay the list of + * resources. + */ +struct ResTable_overlayable_policy_header +{ + struct ResChunk_header header; + + enum PolicyFlags { + // Any overlay can overlay these resources. + POLICY_PUBLIC = 0x00000001, + + // The overlay must reside of the system partition or must have existed on the system partition + // before an upgrade to overlay these resources. + POLICY_SYSTEM_PARTITION = 0x00000002, + + // The overlay must reside of the vendor partition or must have existed on the vendor partition + // before an upgrade to overlay these resources. + POLICY_VENDOR_PARTITION = 0x00000004, + + // The overlay must reside of the product partition or must have existed on the product + // partition before an upgrade to overlay these resources. + POLICY_PRODUCT_PARTITION = 0x00000008, + + // The overlay must reside of the product services partition or must have existed on the product + // services partition before an upgrade to overlay these resources. + POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010, + }; + uint32_t policy_flags; + + // The number of ResTable_ref that follow this header. + uint32_t entry_count; +}; + struct alignas(uint32_t) Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index ffa48367c252..441356b95d36 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -22,12 +22,14 @@ #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" +#include "data/overlayable/R.h" #include "data/sparse/R.h" #include "data/styles/R.h" namespace app = com::android::app; namespace basic = com::android::basic; namespace libclient = com::android::libclient; +namespace overlayable = com::android::overlayable; namespace sparse = com::android::sparse; using ::android::base::ReadFileToString; @@ -273,10 +275,44 @@ TEST(LoadedArscTest, LoadOverlay) { ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } -// structs with size fields (like Res_value, ResTable_entry) should be -// backwards and forwards compatible (aka checking the size field against -// sizeof(Res_value) might not be backwards compatible. -TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } +TEST(LoadedArscTest, LoadOverlayable) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", + "resources.arsc", &contents)); + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, + false /*load_as_shared_library*/); + + ASSERT_THAT(loaded_arsc, NotNull()); + const LoadedPackage* package = loaded_arsc->GetPackageById( + get_package_id(overlayable::R::string::not_overlayable)); + + const OverlayableInfo* info = package->GetOverlayableInfo( + overlayable::R::string::not_overlayable); + ASSERT_THAT(info, IsNull()); + + info = package->GetOverlayableInfo(overlayable::R::string::overlayable1); + ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + + info = package->GetOverlayableInfo(overlayable::R::string::overlayable2); + ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->policy_flags, + Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION + | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + + info = package->GetOverlayableInfo(overlayable::R::string::overlayable3); + ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->policy_flags, + Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION + | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION + | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + + info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); + ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); +} TEST(LoadedArscTest, ResourceIdentifierIterator) { std::string contents; @@ -326,4 +362,9 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { ASSERT_EQ(end, iter); } +// structs with size fields (like Res_value, ResTable_entry) should be +// backwards and forwards compatible (aka checking the size field against +// sizeof(Res_value) might not be backwards compatible. +TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } + } // namespace android diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex 33f961117c44..d37874dcbb40 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlayable/AndroidManifest.xml b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml new file mode 100644 index 000000000000..abc2a454e845 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/AndroidManifest.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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlayable"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h new file mode 100644 index 000000000000..e46e264da318 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/R.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef TESTS_DATA_OVERLAYABLE_R_H_ +#define TESTS_DATA_OVERLAYABLE_R_H_ + +#include <cstdint> + +namespace com { +namespace android { +namespace overlayable { + +struct R { + struct string { + enum : uint32_t { + not_overlayable = 0x7f010000, + overlayable1 = 0x7f010001, + overlayable2 = 0x7f010002, + overlayable3 = 0x7f010003, + overlayable4 = 0x7f010004, + }; + }; +}; + +} // namespace overlayable +} // namespace android +} // namespace com + +#endif /* TESTS_DATA_OVERLAYABLE_R_H_ */ diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build new file mode 100755 index 000000000000..98fdc5101160 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/build @@ -0,0 +1,22 @@ +#!/bin/bash +# +# 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. +# + +set -e + +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk Binary files differnew file mode 100644 index 000000000000..85ab4be7a2e5 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/overlayable.apk diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml new file mode 100644 index 000000000000..11aa7354901d --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -0,0 +1,41 @@ +<?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> +<overlayable> + <!-- Any overlay can overlay the value of @string/overlayable1 --> + <item type="string" name="overlayable1" /> + + <!-- Any overlay on the product or system partition can overlay the value of + @string/overlayable2 --> + <policy type="product|system"> + <item type="string" name="overlayable2" /> + </policy> + + <!-- Any overlay can overlay the value of @string/overlayable4 --> + <policy type="public"> + <item type="string" name="overlayable4" /> + </policy> +</overlayable> + +<overlayable> + <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of + @string/overlayable3 --> + <policy type="product_services|vendor|product"> + <item type="string" name="overlayable3" /> + </policy> +</overlayable> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml new file mode 100644 index 000000000000..5676d7cc64c9 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml @@ -0,0 +1,23 @@ +<?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> + <public type="string" name="not_overlayable" id="0x7f010000" /> + <public type="string" name="overlayable1" id="0x7f010001" /> + <public type="string" name="overlayable2" id="0x7f010002" /> + <public type="string" name="overlayable3" id="0x7f010003" /> + <public type="string" name="overlayable4" id="0x7f010004" /> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml new file mode 100644 index 000000000000..a86b31282bc9 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml @@ -0,0 +1,23 @@ +<?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> + <string name="not_overlayable">Not overlayable</string> + <string name="overlayable1">Overlayable One</string> + <string name="overlayable2">Overlayable Two</string> + <string name="overlayable3">Overlayable Three</string> + <string name="overlayable4">Overlayable Four</string> +</resources> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index da77b99e6e53..ed7d5e578693 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -171,6 +171,7 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", + "pipeline/skia/VkFunctorDrawable.cpp", "pipeline/skia/VkInteropFunctorDrawable.cpp", "renderstate/RenderState.cpp", "renderthread/CacheManager.cpp", diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 4a3e10c54cef..8067313b2cb2 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -29,8 +29,6 @@ namespace android { namespace uirenderer { -bool Properties::drawDeferDisabled = false; -bool Properties::drawReorderDisabled = false; bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; bool Properties::showDirtyRegions = false; @@ -40,7 +38,6 @@ bool Properties::enablePartialUpdates = true; DebugLevel Properties::debugLevel = kDebugDisabled; OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default; -StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide; float Properties::overrideLightRadius = -1.0f; float Properties::overrideLightPosY = -1.0f; @@ -85,7 +82,6 @@ bool Properties::load() { char property[PROPERTY_VALUE_MAX]; bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; - StencilClipDebug prevDebugStencilClip = debugStencilClip; debugOverdraw = false; if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { @@ -99,20 +95,6 @@ bool Properties::load() { } } - // See Properties.h for valid values - if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) { - INIT_LOGD(" Stencil clip debug enabled: %s", property); - if (!strcmp(property, "hide")) { - debugStencilClip = StencilClipDebug::Hide; - } else if (!strcmp(property, "highlight")) { - debugStencilClip = StencilClipDebug::ShowHighlight; - } else if (!strcmp(property, "region")) { - debugStencilClip = StencilClipDebug::ShowRegion; - } - } else { - debugStencilClip = StencilClipDebug::Hide; - } - sProfileType = ProfileType::None; if (property_get(PROPERTY_PROFILE, property, "") > 0) { if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) { @@ -125,12 +107,6 @@ bool Properties::load() { debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false); INIT_LOGD(" Layers updates debug enabled: %d", debugLayersUpdates); - drawDeferDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_DEFER, false); - INIT_LOGD(" Draw defer %s", drawDeferDisabled ? "disabled" : "enabled"); - - drawReorderDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_REORDER, false); - INIT_LOGD(" Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled"); - showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled); @@ -152,8 +128,7 @@ bool Properties::load() { enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true); - return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) || - (prevDebugStencilClip != debugStencilClip); + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } void Properties::overrideProperty(const char* name, const char* value) { diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index da53f6657ff7..0a7f4e7eb41c 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -95,20 +95,6 @@ enum DebugLevel { #define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars" /** - * Used to enable/disable non-rectangular clipping debugging. - * - * The accepted values are: - * "highlight", drawing commands clipped by the stencil will - * be colored differently - * "region", renders the clipping region on screen whenever - * the stencil is set - * "hide", don't show the clip - * - * The default value is "hide". - */ -#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip" - -/** * Turn on to draw dirty regions every other frame. * * Possible values: @@ -118,19 +104,6 @@ enum DebugLevel { #define PROPERTY_DEBUG_SHOW_DIRTY_REGIONS "debug.hwui.show_dirty_regions" /** - * Disables draw operation deferral if set to "true", forcing draw - * commands to be issued to OpenGL in order, and processed in sequence - * with state-manipulation canvas commands. - */ -#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer" - -/** - * Used to disable draw operation reordering when deferring draw operations - * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true" - */ -#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder" - -/** * Setting this property will enable or disable the dropping of frames with * empty damage. Default is "true". */ @@ -207,8 +180,6 @@ enum class ProfileType { None, Console, Bars }; enum class OverdrawColorSet { Default = 0, Deuteranomaly }; -enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion }; - enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 }; /** @@ -220,8 +191,6 @@ class Properties { public: static bool load(); - static bool drawDeferDisabled; - static bool drawReorderDisabled; static bool debugLayersUpdates; static bool debugOverdraw; static bool showDirtyRegions; @@ -235,7 +204,6 @@ public: static DebugLevel debugLevel; static OverdrawColorSet overdrawColorSet; - static StencilClipDebug debugStencilClip; // Override the value for a subset of properties in this class static void overrideProperty(const char* name, const char* value); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index ba343841d760..6be7ef72cf5d 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -681,7 +681,7 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& p if (mPaintFilter) { mPaintFilter->filter(&paintCopy); } - SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); + SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding); // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and // older. if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 && @@ -708,7 +708,7 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, if (mPaintFilter) { mPaintFilter->filter(&paintCopy); } - SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); + SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding); const int N = end - start; SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform))); diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index 769fce498a70..84292c8768c1 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -43,7 +43,7 @@ MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontDat static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint, const minikin::MinikinPaint& paint, const minikin::FontFakery& fakery) { - skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + skPaint->setTextEncoding(kGlyphID_SkTextEncoding); skPaint->setTextSize(paint.size); skPaint->setTextScaleX(paint.scaleX); skPaint->setTextSkewX(paint.skewX); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index b682ab0256dd..b56c3ef94791 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -25,6 +25,7 @@ #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/GLFunctorDrawable.h" #include "pipeline/skia/VkInteropFunctorDrawable.h" +#include "pipeline/skia/VkFunctorDrawable.h" namespace android { namespace uirenderer { @@ -124,6 +125,8 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) { FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the + // interop is disabled/moved. functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, listener, asSkCanvas()); } else { diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp new file mode 100644 index 000000000000..71ad5e17301a --- /dev/null +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#include "VkFunctorDrawable.h" +#include <private/hwui/DrawVkInfo.h> + +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" +#include <GrBackendDrawableInfo.h> +#include <thread> +#include <utils/Color.h> +#include <utils/Trace.h> +#include <utils/TraceUtils.h> +#include <SkImage.h> +#include <vk/GrVkTypes.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor) + : INHERITED() + , mFunctor(functor) {} + +VkFunctorDrawHandler::~VkFunctorDrawHandler() { + // TODO(cblume) Fill in the DrawVkInfo parameters. + (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr); +} + +void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { + ATRACE_CALL(); + + GrVkDrawableInfo vulkan_info; + if (!info.getVkDrawableInfo(&vulkan_info)) { + return; + } + + DrawVkInfo draw_vk_info; + // TODO(cblume) Fill in the rest of the parameters and test the actual call. + draw_vk_info.isLayer = true; + + (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info); +} + +VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, + SkCanvas* canvas) + : FunctorDrawable(functor, listener, canvas) {} + +VkFunctorDrawable::~VkFunctorDrawable() = default; + +void VkFunctorDrawable::syncFunctor() const { + (*mFunctor)(DrawVkInfo::kModeSync, nullptr); +} + +void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { + LOG_ALWAYS_FATAL("VkFunctorDrawable::onDraw() should never be called."); + // Instead of calling onDraw(), the call should come from onSnapGpuDrawHandler. +} + +std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler( + GrBackendApi backendApi, const SkMatrix& matrix) { + if (backendApi != GrBackendApi::kVulkan) { + return nullptr; + } + std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor)); + return std::move(draw); +} + +} // namespace skiapipeline +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h new file mode 100644 index 000000000000..5cd131405d15 --- /dev/null +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "FunctorDrawable.h" + +#include <utils/RefBase.h> +#include <ui/GraphicBuffer.h> +#include <SkImageInfo.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +/** + * This draw handler will be returned by VkFunctorDrawable's onSnapGpuDrawHandler. It allows us to + * issue Vulkan commands while the command buffer is being flushed. + */ +class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler { +public: + explicit VkFunctorDrawHandler(Functor* functor); + ~VkFunctorDrawHandler() override; + + void draw(const GrBackendDrawableInfo& info) override; +private: + typedef GpuDrawHandler INHERITED; + + Functor* mFunctor; +}; + +/** + * This drawable wraps a Vulkan functor enabling it to be recorded into a list of Skia drawing + * commands. + */ +class VkFunctorDrawable : public FunctorDrawable { +public: + VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, + SkCanvas* canvas); + ~VkFunctorDrawable() override; + + void syncFunctor() const override; + +protected: + // SkDrawable functions: + void onDraw(SkCanvas* canvas) override; + std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi, + const SkMatrix& matrix) override; +}; + +} // namespace skiapipeline +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index 004a558dd9d0..82285501cb63 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -46,7 +46,7 @@ public: ScopedDrawRequest() { beginDraw(); } private: void beginDraw() { - std::lock_guard{sLock}; + std::lock_guard _lock{sLock}; if (!sGLDrawThread) { sGLDrawThread = new ThreadBase{}; diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h new file mode 100644 index 000000000000..019950fcbc02 --- /dev/null +++ b/libs/hwui/private/hwui/DrawVkInfo.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_DRAW_VK_INFO_H +#define ANDROID_HWUI_DRAW_VK_INFO_H + +#include <vulkan/vulkan.h> + +namespace android { +namespace uirenderer { + +/** + * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan + * functors. + */ +struct DrawVkInfo { + // Input: current width/height of destination surface + int width; + int height; + + // Input: is the render target an FBO + bool isLayer; + + // Input: current transform matrix, in OpenGL format + float transform[16]; + + // Input: WebView should do its main compositing draws into this. It cannot do anything that + // would require stopping the render pass. + VkCommandBuffer secondaryCommandBuffer; + + // Input: The main color attachment index where secondaryCommandBuffer will eventually be + // submitted. + uint32_t colorAttachmentIndex; + + // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer + // will be submitted into. + VkRenderPass compatibleRenderPass; + + // Input: Format of the destination surface. + VkFormat format; + + // Input: Color space transfer params + float G; + float A; + float B; + float C; + float D; + float E; + float F; + + // Input: Color space transformation from linear RGB to D50-adapted XYZ + float matrix[9]; + + // Input: current clip rect + int clipLeft; + int clipTop; + int clipRight; + int clipBottom; + + /** + * Values used as the "what" parameter of the functor. + */ + enum Mode { + // Called once at WebView start + kModeInit, + // Called when things need to be re-created + kModeReInit, + // Notifies the app that the composite functor will be called soon. This allows WebView to + // begin work early. + kModePreComposite, + // Do the actual composite work + kModeComposite, + // This allows WebView to begin using the previously submitted objects in future work. + kModePostComposite, + // Invoked every time the UI thread pushes over a frame to the render thread and the owning + // view has a dirty display list*. This is a signal to sync any data that needs to be + // shared between the UI thread and the render thread. During this time the UI thread is + // blocked. + kModeSync + }; + + /** + * Values used by Vulkan functors to tell the framework what to do next. + */ + enum Status { + // The functor is done + kStatusDone = 0x0, + }; +}; // struct DrawVkInfo + +} // namespace uirenderer +} // namespace android + +#endif // ANDROID_HWUI_DRAW_VK_INFO_H diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index e1f8307343a7..2abb3d5179a0 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -745,7 +745,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { surface->mWindowWidth = extent.width; surface->mWindowHeight = extent.height; - uint32_t imageCount = caps.minImageCount + 2; + uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount); if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) { // Application must settle for fewer images than desired: imageCount = caps.maxImageCount; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 8a1bc4d2f7f2..f81202292a49 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -81,7 +81,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint auto utf16 = asciiToUtf16(text); uint32_t length = strlen(text); SkPaint glyphPaint(paint); - glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding); canvas->drawText( utf16.get(), length, // text buffer 0, length, // draw range @@ -94,7 +94,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint const SkPath& path) { auto utf16 = asciiToUtf16(text); SkPaint glyphPaint(paint); - glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding); canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint, nullptr); } diff --git a/media/OWNERS b/media/OWNERS index 0abf9aeb6101..03b751c07c6c 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -1,12 +1,13 @@ +chz@google.com +dwkang@google.com elaurent@google.com etalvala@google.com gkasten@google.com hunga@google.com +jaewan@google.com jmtrivi@google.com +jsharkey@android.com lajos@google.com marcone@google.com sungsoo@google.com wjia@google.com -jaewan@google.com -chz@google.com -dwkang@google.com diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java index 0e8e6ceeaa15..cb9669bb653d 100644 --- a/media/java/android/media/CallbackDataSourceDesc.java +++ b/media/java/android/media/CallbackDataSourceDesc.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.NonNull; /** - * @hide * Structure of data source descriptor for sources using callback. * * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, diff --git a/media/java/android/media/DataSourceCallback.java b/media/java/android/media/DataSourceCallback.java index 9b27baf32204..0d4d53106c78 100644 --- a/media/java/android/media/DataSourceCallback.java +++ b/media/java/android/media/DataSourceCallback.java @@ -21,7 +21,6 @@ import java.io.Closeable; 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/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java index 702034e987ca..9109ea5bdab2 100644 --- a/media/java/android/media/DataSourceDesc.java +++ b/media/java/android/media/DataSourceDesc.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.NonNull; /** - * @hide * Base class of data source descriptor. * * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, @@ -36,6 +35,9 @@ public class DataSourceDesc { // keep consistent with native code public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000; + /** + * @hide + */ public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000; public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS; @@ -172,7 +174,8 @@ public class DataSourceDesc { /** * Sets the end position in milliseconds at which the playback will end. - * Any negative number is treated as maximum length of the data source. + * Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS} + * of the data source * * @param position the end position in milliseconds at which the playback will end * @return the same Builder instance. diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java index 763a81f53765..14ef18029233 100644 --- a/media/java/android/media/FileDataSourceDesc.java +++ b/media/java/android/media/FileDataSourceDesc.java @@ -23,7 +23,6 @@ import android.util.Log; import java.io.IOException; /** - * @hide * Structure of data source descriptor for sources using file descriptor. * * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 3d13c5f72f55..b7743c9db4c0 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1045,11 +1045,11 @@ public final class MediaFormat { /** * Returns the value of a numeric key. This is provided as a convenience method for keys - * that may take multiple numeric types, such as {@link KEY_FRAME_RATE}, or {@link - * KEY_I_FRAME_INTERVAL}. + * that may take multiple numeric types, such as {@link #KEY_FRAME_RATE}, or {@link + * #KEY_I_FRAME_INTERVAL}. * * @return null if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is ByteBuffer or String + * @throws ClassCastException if the stored value for the key is ByteBuffer or String */ public final @Nullable Number getNumber(@NonNull String name) { return ((Number)mMap.get(name)); @@ -1059,7 +1059,7 @@ public final class MediaFormat { * Returns the value of a numeric key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is ByteBuffer or String + * @throws ClassCastException if the stored value for the key is ByteBuffer or String */ public final @NonNull Number getNumber(@NonNull String name, @NonNull Number defaultValue) { Number ret = getNumber(name); @@ -1069,9 +1069,10 @@ public final class MediaFormat { /** * Returns the value of an integer key. * - * @throw NullPointerException if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is long, float, ByteBuffer or - * String + * @throws NullPointerException if the key does not exist or the stored value for the key is + * null + * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or + * String */ public final int getInteger(@NonNull String name) { return ((Integer)mMap.get(name)).intValue(); @@ -1081,8 +1082,8 @@ public final class MediaFormat { * Returns the value of an integer key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is long, float, ByteBuffer or - * String + * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or + * String */ public final int getInteger(@NonNull String name, int defaultValue) { try { @@ -1096,9 +1097,10 @@ public final class MediaFormat { /** * Returns the value of a long key. * - * @throw NullPointerException if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, float, ByteBuffer or - * String + * @throws NullPointerException if the key does not exist or the stored value for the key is + * null + * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or + * String */ public final long getLong(@NonNull String name) { return ((Long)mMap.get(name)).longValue(); @@ -1108,8 +1110,8 @@ public final class MediaFormat { * Returns the value of an long key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, float, ByteBuffer or - * String + * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or + * String */ public final long getLong(@NonNull String name, long defaultValue) { try { @@ -1123,9 +1125,10 @@ public final class MediaFormat { /** * Returns the value of a float key. * - * @throw NullPointerException if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, ByteBuffer or - * String + * @throws NullPointerException if the key does not exist or the stored value for the key is + * null + * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or + * String */ public final float getFloat(@NonNull String name) { return ((Float)mMap.get(name)).floatValue(); @@ -1135,8 +1138,8 @@ public final class MediaFormat { * Returns the value of an float key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, ByteBuffer or - * String + * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or + * String */ public final float getFloat(@NonNull String name, float defaultValue) { try { @@ -1151,7 +1154,7 @@ public final class MediaFormat { * Returns the value of a string key. * * @return null if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, float or ByteBuffer + * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer */ public final @Nullable String getString(@NonNull String name) { return (String)mMap.get(name); @@ -1161,7 +1164,7 @@ public final class MediaFormat { * Returns the value of an string key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, float or ByteBuffer + * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer */ public final @NonNull String getString(@NonNull String name, @NonNull String defaultValue) { String ret = getString(name); @@ -1172,7 +1175,7 @@ public final class MediaFormat { * Returns the value of a ByteBuffer key. * * @return null if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, float or String + * @throws ClassCastException if the stored value for the key is int, long, float or String */ public final @Nullable ByteBuffer getByteBuffer(@NonNull String name) { return (ByteBuffer)mMap.get(name); @@ -1182,7 +1185,7 @@ public final class MediaFormat { * Returns the value of a ByteBuffer key, or the default value if the key is missing. * * @return defaultValue if the key does not exist or the stored value for the key is null - * @throw ClassCastException if the stored value for the key is int, long, float or String + * @throws ClassCastException if the stored value for the key is int, long, float or String */ public final @NonNull ByteBuffer getByteBuffer( @NonNull String name, @NonNull ByteBuffer defaultValue) { @@ -1197,7 +1200,7 @@ public final class MediaFormat { * @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature. * * @throws IllegalArgumentException if the feature was neither set to be enabled - * nor to be disabled. + * nor to be disabled. */ public boolean getFeatureEnabled(@NonNull String feature) { Integer enabled = (Integer)mMap.get(KEY_FEATURE_ + feature); diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index a10b2123a70c..e6ad4441401a 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -21,9 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; -import android.annotation.UnsupportedAppUsage; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningAppProcessInfo; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; @@ -81,8 +78,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** - * @hide - * * MediaPlayer2 class can be used to control playback of audio/video files and streams. * * <p>Topics covered here are: @@ -776,7 +771,7 @@ public class MediaPlayer2 implements AutoCloseable * * @return the current DataSourceDesc */ - public DataSourceDesc getCurrentDataSource() { + public @Nullable DataSourceDesc getCurrentDataSource() { synchronized (mSrcLock) { return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD; } @@ -1252,19 +1247,18 @@ public class MediaPlayer2 implements AutoCloseable * * <p>This function has the MediaPlayer2 access the low-level power manager * service to control the device's power usage while playing is occurring. - * The parameter is a combination of {@link android.os.PowerManager} wake flags. + * The parameter is a {@link android.os.PowerManager.WakeLock}. * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK} * permission. * By default, no attempt is made to keep the device awake during playback. * - * @param context the Context to use - * @param mode the power/wake mode to set + * @param wakeLock the power wake lock used during playback. * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. * @see android.os.PowerManager */ // This is an asynchronous call. - public Object setWakeMode(Context context, int mode) { - return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) { + public Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) { + return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) { @Override void process() { boolean washeld = false; @@ -1274,28 +1268,15 @@ public class MediaPlayer2 implements AutoCloseable washeld = true; mWakeLock.release(); } - mWakeLock = null; } - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - ActivityManager am = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses(); - int pid = android.os.Process.myPid(); - String name = "pid " + String.valueOf(pid); - if (runningAppsProcInfo != null) { - for (RunningAppProcessInfo procInfo : runningAppsProcInfo) { - if (procInfo.pid == pid) { - name = procInfo.processName; - break; - } + mWakeLock = wakeLock; + if (mWakeLock != null) { + mWakeLock.setReferenceCounted(false); + if (washeld) { + mWakeLock.acquire(); } } - mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name); - mWakeLock.setReferenceCounted(false); - if (washeld) { - mWakeLock.acquire(); - } } }); } @@ -1303,7 +1284,7 @@ public class MediaPlayer2 implements AutoCloseable /** * Control whether we should use the attached SurfaceHolder to keep the * screen on while video playback is occurring. This is the preferred - * method over {@link #setWakeMode} where possible, since it doesn't + * method over {@link #setWakeLock} where possible, since it doesn't * require that the application have permission for low-level wake lock * access. * @@ -1350,9 +1331,13 @@ public class MediaPlayer2 implements AutoCloseable * * @param token the command to be canceled. This is the returned Object when command is issued. * @return {@code false} if the task could not be cancelled; {@code true} otherwise. + * @throws IllegalArgumentException if argument token is null. */ // This is a synchronous call. - public boolean cancelCommand(Object token) { + public boolean cancelCommand(@NonNull Object token) { + if (token == null) { + throw new IllegalArgumentException("command token should not be null"); + } synchronized (mTaskLock) { return mPendingTasks.remove(token); } @@ -1891,7 +1876,6 @@ public class MediaPlayer2 implements AutoCloseable * Gets the track type. * @return TrackType which indicates if the track is video, audio, timed text. */ - @UnsupportedAppUsage public int getTrackType() { return mTrackType; } @@ -1902,7 +1886,6 @@ public class MediaPlayer2 implements AutoCloseable * When the language is unknown or could not be determined, * ISO-639-2 language code, "und", is returned. */ - @UnsupportedAppUsage public String getLanguage() { String language = mFormat.getString(MediaFormat.KEY_LANGUAGE); return language == null ? "und" : language; @@ -1933,19 +1916,20 @@ public class MediaPlayer2 implements AutoCloseable final int mTrackType; final MediaFormat mFormat; - TrackInfo(Iterator<Value> in) { - mTrackType = in.next().getInt32Value(); + static TrackInfo create(Iterator<Value> in) { + int trackType = in.next().getInt32Value(); // TODO: build the full MediaFormat; currently we are using createSubtitleFormat // even for audio/video tracks, meaning we only set the mime and language. String mime = in.next().getStringValue(); String language = in.next().getStringValue(); - mFormat = MediaFormat.createSubtitleFormat(mime, language); + MediaFormat format = MediaFormat.createSubtitleFormat(mime, language); - if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value()); - mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value()); - mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value()); + if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) { + format.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value()); + format.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value()); + format.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value()); } + return new TrackInfo(trackType, format); } /** @hide */ @@ -1990,9 +1974,9 @@ public class MediaPlayer2 implements AutoCloseable * addTimedTextSource method is called. * @throws IllegalStateException if it is called in an invalid state. */ - public List<TrackInfo> getTrackInfo() { + public @NonNull List<TrackInfo> getTrackInfo() { TrackInfo[] trackInfo = getInbandTrackInfo(); - return Arrays.asList(trackInfo); + return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0)); } private TrackInfo[] getInbandTrackInfo() throws IllegalStateException { @@ -2010,7 +1994,7 @@ public class MediaPlayer2 implements AutoCloseable } TrackInfo[] trackInfo = new TrackInfo[size]; for (int i = 0; i < size; ++i) { - trackInfo[i] = new TrackInfo(in); + trackInfo[i] = TrackInfo.create(in); } return trackInfo; } @@ -2505,7 +2489,7 @@ public class MediaPlayer2 implements AutoCloseable Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj); break; } - DrmInfo drmInfo = new DrmInfo(playerMsg); + DrmInfo drmInfo = DrmInfo.create(playerMsg); synchronized (sourceInfo) { sourceInfo.mDrmInfo = drmInfo; } @@ -2556,7 +2540,7 @@ public class MediaPlayer2 implements AutoCloseable * @param size the size of the video */ public void onVideoSizeChanged( - MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) { } + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull VideoSize size) { } /** * Called to indicate an avaliable timed text @@ -2567,7 +2551,8 @@ public class MediaPlayer2 implements AutoCloseable * needed to be displayed and the display format. * @hide */ - public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { } + public void onTimedText( + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull TimedText text) { } /** * Called to indicate avaliable timed metadata @@ -2588,7 +2573,8 @@ public class MediaPlayer2 implements AutoCloseable * @param data the timed metadata sample associated with this event */ public void onTimedMetaDataAvailable( - MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { } + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull TimedMetaData data) { } /** * Called to indicate an error. @@ -2600,7 +2586,8 @@ public class MediaPlayer2 implements AutoCloseable * implementation dependent. */ public void onError( - MediaPlayer2 mp, DataSourceDesc dsd, @MediaError int what, int extra) { } + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @MediaError int what, int extra) { } /** * Called to indicate an info or a warning. @@ -2611,7 +2598,9 @@ public class MediaPlayer2 implements AutoCloseable * @param extra an extra code, specific to the info. Typically * implementation dependent. */ - public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, @MediaInfo int what, int extra) { } + public void onInfo( + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @MediaInfo int what, int extra) { } /** * Called to acknowledge an API call. @@ -2622,7 +2611,7 @@ public class MediaPlayer2 implements AutoCloseable * @param status the returned status code for the call. */ public void onCallCompleted( - MediaPlayer2 mp, DataSourceDesc dsd, @CallCompleted int what, + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @CallCompleted int what, @CallStatus int status) { } /** @@ -2633,7 +2622,8 @@ public class MediaPlayer2 implements AutoCloseable * @param timestamp the new media clock. */ public void onMediaTimeDiscontinuity( - MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { } + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull MediaTimestamp timestamp) { } /** * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed. @@ -2642,7 +2632,7 @@ public class MediaPlayer2 implements AutoCloseable * @param label the application specific Object given by * {@link #notifyWhenCommandLabelReached(Object)}. */ - public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { } + public void onCommandLabelReached(@NonNull MediaPlayer2 mp, @NonNull Object label) { } /** * Called when when a player subtitle track has new subtitle data available. @@ -2651,7 +2641,8 @@ public class MediaPlayer2 implements AutoCloseable * @param data the subtitle data */ public void onSubtitleData( - MediaPlayer2 mp, DataSourceDesc dsd, @NonNull SubtitleData data) { } + @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull SubtitleData data) { } } private final Object mEventCbLock = new Object(); @@ -3046,10 +3037,10 @@ public class MediaPlayer2 implements AutoCloseable */ public static final int CALL_COMPLETED_SET_DISPLAY = 33; - /** The player just completed a call {@link #setWakeMode}. + /** The player just completed a call {@link #setWakeLock}. * @see EventCallback#onCallCompleted */ - public static final int CALL_COMPLETED_SET_WAKE_MODE = 34; + public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34; /** The player just completed a call {@link #setScreenOnWhilePlaying}. * @see EventCallback#onCallCompleted @@ -3102,7 +3093,7 @@ public class MediaPlayer2 implements AutoCloseable CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, CALL_COMPLETED_SET_BUFFERING_PARAMS, CALL_COMPLETED_SET_DISPLAY, - CALL_COMPLETED_SET_WAKE_MODE, + CALL_COMPLETED_SET_WAKE_LOCK, CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, CALL_COMPLETED_PREPARE_DRM, @@ -3179,6 +3170,7 @@ public class MediaPlayer2 implements AutoCloseable * The only allowed DRM calls in this listener are * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)} * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}. + * @hide */ public interface OnDrmConfigHelper { /** @@ -3197,6 +3189,7 @@ public class MediaPlayer2 implements AutoCloseable * of {@link #prepareDrm(DataSourceDesc, UUID)}. * * @param listener the callback that will be run + * @hide */ // This is a synchronous call. public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { @@ -3208,6 +3201,7 @@ public class MediaPlayer2 implements AutoCloseable /** * Interface definition for callbacks to be invoked when the player has the corresponding * DRM events. + * @hide */ public static class DrmEventCallback { /** @@ -3241,6 +3235,7 @@ public class MediaPlayer2 implements AutoCloseable * * @param eventCallback the callback that will be run * @param executor the executor through which the callback should be invoked + * @hide */ // This is a synchronous call. public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor, @@ -3261,6 +3256,7 @@ public class MediaPlayer2 implements AutoCloseable * Unregisters the {@link DrmEventCallback}. * * @param eventCallback the callback to be unregistered + * @hide */ // This is a synchronous call. public void unregisterDrmEventCallback(DrmEventCallback eventCallback) { @@ -3278,31 +3274,37 @@ public class MediaPlayer2 implements AutoCloseable * <p> * * DRM preparation has succeeded. + * @hide */ public static final int PREPARE_DRM_STATUS_SUCCESS = 0; /** * The device required DRM provisioning but couldn't reach the provisioning server. + * @hide */ public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; /** * The device required DRM provisioning but the provisioning server denied the request. + * @hide */ public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; /** * The DRM preparation has failed . + * @hide */ public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; /** * The crypto scheme UUID is not supported by the device. + * @hide */ public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4; /** * The hardware resources are not available, due to being in use. + * @hide */ public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5; @@ -3343,6 +3345,7 @@ public class MediaPlayer2 implements AutoCloseable * @param dsd The DRM protected data source * * @throws IllegalStateException if called before being prepared + * @hide */ public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) { final SourceInfo sourceInfo = getSourceInfo(dsd); @@ -3398,6 +3401,7 @@ public class MediaPlayer2 implements AutoCloseable * {@link DrmEventCallback#onDrmInfo}. * * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. + * @hide */ // This is an asynchronous call. public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) { @@ -3491,6 +3495,7 @@ public class MediaPlayer2 implements AutoCloseable * @param dsd The DRM protected data source * * @throws NoDrmSchemeException if there is no active DRM session to release + * @hide */ // This is a synchronous call. public void releaseDrm(@NonNull DataSourceDesc dsd) @@ -3501,7 +3506,7 @@ public class MediaPlayer2 implements AutoCloseable } } - private native void native_releaseDrm(); + private native void native_releaseDrm(long mSrcId); /** * A key request/response exchange occurs between the app and a license server @@ -3541,6 +3546,7 @@ public class MediaPlayer2 implements AutoCloseable * This may be {@code null} if no additional parameters are to be sent. * * @throws NoDrmSchemeException if there is no active DRM session + * @hide */ public MediaDrm.KeyRequest getDrmKeyRequest( @NonNull DataSourceDesc dsd, @@ -3581,6 +3587,7 @@ public class MediaPlayer2 implements AutoCloseable * @throws NoDrmSchemeException if there is no active DRM session * @throws DeniedByServerException if the response indicates that the * server rejected the request + * @hide */ // This is a synchronous call. public byte[] provideDrmKeyResponse( @@ -3606,6 +3613,7 @@ public class MediaPlayer2 implements AutoCloseable * @param keySetId identifies the saved key set to restore * * @throws NoDrmSchemeException if there is no active DRM session + * @hide */ // This is a synchronous call. public void restoreDrmKeys( @@ -3633,6 +3641,7 @@ public class MediaPlayer2 implements AutoCloseable * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} * * @throws NoDrmSchemeException if there is no active DRM session + * @hide */ public String getDrmPropertyString( @NonNull DataSourceDesc dsd, @@ -3659,6 +3668,7 @@ public class MediaPlayer2 implements AutoCloseable * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} * * @throws NoDrmSchemeException if there is no active DRM session + * @hide */ // This is a synchronous call. public void setDrmPropertyString( @@ -3676,6 +3686,7 @@ public class MediaPlayer2 implements AutoCloseable /** * Encapsulates the DRM properties of the source. + * @hide */ public static final class DrmInfo { private Map<UUID, byte[]> mMapPssh; @@ -3702,36 +3713,37 @@ public class MediaPlayer2 implements AutoCloseable mSupportedSchemes = supportedSchemes; } - private DrmInfo(PlayerMessage msg) { - Log.v(TAG, "DrmInfo(" + msg + ")"); + private static DrmInfo create(PlayerMessage msg) { + Log.v(TAG, "DrmInfo.create(" + msg + ")"); Iterator<Value> in = msg.getValuesList().iterator(); byte[] pssh = in.next().getBytesValue().toByteArray(); - Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh)); - mMapPssh = parsePSSH(pssh, pssh.length); - Log.v(TAG, "DrmInfo() PSSH: " + mMapPssh); + Log.v(TAG, "DrmInfo.create() PSSH: " + DrmInfo.arrToHex(pssh)); + Map<UUID, byte[]> mapPssh = DrmInfo.parsePSSH(pssh, pssh.length); + Log.v(TAG, "DrmInfo.create() PSSH: " + mapPssh); int supportedDRMsCount = in.next().getInt32Value(); - mSupportedSchemes = new UUID[supportedDRMsCount]; + UUID[] supportedSchemes = new UUID[supportedDRMsCount]; for (int i = 0; i < supportedDRMsCount; i++) { byte[] uuid = new byte[16]; in.next().getBytesValue().copyTo(uuid, 0); - mSupportedSchemes[i] = bytesToUUID(uuid); + supportedSchemes[i] = DrmInfo.bytesToUUID(uuid); - Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + mSupportedSchemes[i]); + Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + supportedSchemes[i]); } - Log.v(TAG, "DrmInfo() psshsize: " + pssh.length + Log.v(TAG, "DrmInfo.create() psshsize: " + pssh.length + " supportedDRMsCount: " + supportedDRMsCount); + return new DrmInfo(mapPssh, supportedSchemes); } private DrmInfo makeCopy() { return new DrmInfo(this.mMapPssh, this.mSupportedSchemes); } - private String arrToHex(byte[] bytes) { + private static String arrToHex(byte[] bytes) { String out = "0x"; for (int i = 0; i < bytes.length; i++) { out += String.format("%02x", bytes[i]); @@ -3740,7 +3752,7 @@ public class MediaPlayer2 implements AutoCloseable return out; } - private UUID bytesToUUID(byte[] uuid) { + private static UUID bytesToUUID(byte[] uuid) { long msb = 0, lsb = 0; for (int i = 0; i < 8; i++) { msb |= (((long) uuid[i] & 0xff) << (8 * (7 - i))); @@ -3750,7 +3762,7 @@ public class MediaPlayer2 implements AutoCloseable return new UUID(msb, lsb); } - private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) { + private static Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) { Map<UUID, byte[]> result = new HashMap<UUID, byte[]>(); final int uuidSize = 16; @@ -3814,6 +3826,7 @@ public class MediaPlayer2 implements AutoCloseable * Thrown when a DRM method is called before preparing a DRM scheme through * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}. * Extends MediaDrm.MediaDrmException + * @hide */ public static final class NoDrmSchemeException extends MediaDrmException { public NoDrmSchemeException(String detailMessage) { @@ -3821,7 +3834,8 @@ public class MediaPlayer2 implements AutoCloseable } } - private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId); + private native void native_prepareDrm( + long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId); // Instantiated from the native side @SuppressWarnings("unused") @@ -4064,6 +4078,7 @@ public class MediaPlayer2 implements AutoCloseable static final int PROVISION_TIMEOUT_MS = 60000; final DataSourceDesc mDSD; + final long mSrcId; //--- guarded by |this| start MediaDrm mDrmObj; @@ -4075,8 +4090,9 @@ public class MediaPlayer2 implements AutoCloseable Future<?> mProvisionResult; //--- guarded by |this| end - DrmHandle(DataSourceDesc dsd) { + DrmHandle(DataSourceDesc dsd, long srcId) { mDSD = dsd; + mSrcId = srcId; } void prepare(UUID uuid) throws UnsupportedSchemeException, @@ -4186,7 +4202,8 @@ public class MediaPlayer2 implements AutoCloseable // Sending it down to native/mediaserver to create the crypto object // This call could simply fail due to bad player state, e.g., after play(). - MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId); + final MediaPlayer2 mp2 = MediaPlayer2.this; + mp2.native_prepareDrm(mSrcId, getByteArrayFromUUID(uuid), mDrmSessionId); Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded"); } catch (Exception e) { //ResourceBusyException, NotProvisionedException @@ -4367,7 +4384,7 @@ public class MediaPlayer2 implements AutoCloseable // exception if we're in a non-stopped/prepared state. // for cleaning native/mediaserver crypto object - native_releaseDrm(); + native_releaseDrm(mSrcId); // for cleaning client-side MediaDrm object; only called if above has succeeded cleanDrmObj(); @@ -4573,7 +4590,7 @@ public class MediaPlayer2 implements AutoCloseable SourceInfo(DataSourceDesc dsd) { this.mDSD = dsd; - mDrmHandle = new DrmHandle(dsd); + mDrmHandle = new DrmHandle(dsd, mId); } void close() { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 0950a2497866..90cfc534877e 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -1015,12 +1015,6 @@ public class MediaScanner implements AutoCloseable { // exif is null } if (exif != null) { - float[] latlng = new float[2]; - if (exif.getLatLong(latlng)) { - values.put(Images.Media.LATITUDE, latlng[0]); - values.put(Images.Media.LONGITUDE, latlng[1]); - } - long time = exif.getGpsDateTime(); if (time != -1) { values.put(Images.Media.DATE_TAKEN, time); diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java index ba37b9b66360..852babe1ecea 100644 --- a/media/java/android/media/SubtitleData.java +++ b/media/java/android/media/SubtitleData.java @@ -17,8 +17,11 @@ package android.media; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; +import java.util.Arrays; + /** * Class encapsulating subtitle data, as received through the * {@link MediaPlayer.OnSubtitleDataListener} interface. @@ -80,11 +83,11 @@ public final class SubtitleData } /** @hide */ - public SubtitleData(int trackIndex, long startTimeUs, long durationUs, byte[] data) { + public SubtitleData(int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) { mTrackIndex = trackIndex; mStartTimeUs = startTimeUs; mDurationUs = durationUs; - mData = data; + mData = (data != null ? data : new byte[0]); } /** @@ -138,4 +141,80 @@ public final class SubtitleData return true; } + + /** + * Builder class for {@link SubtitleData} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link SubtitleData}: + * + * <pre class="prettyprint"> + * SubtitleData sd = new SubtitleData.Builder() + * .setSubtitleData(trackIndex, startTime, duration, data) + * .build(); + * </pre> + * @hide + */ + @SystemApi + public static class Builder { + private int mTrackIndex; + private long mStartTimeUs; + private long mDurationUs; + private byte[] mData = new byte[0]; + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + } + + /** + * Constructs a new Builder from a given {@link SubtitleData} instance + * @param sd the {@link SubtitleData} object whose data will be reused + * in the new Builder. It should not be null. The data array is copied. + */ + public Builder(@NonNull SubtitleData sd) { + if (sd == null) { + throw new IllegalArgumentException("null SubtitleData is not allowed"); + } + mTrackIndex = sd.mTrackIndex; + mStartTimeUs = sd.mStartTimeUs; + mDurationUs = sd.mDurationUs; + if (sd.mData != null) { + mData = Arrays.copyOf(sd.mData, sd.mData.length); + } + } + + /** + * Combines all of the fields that have been set and return a new + * {@link SubtitleData} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * + * @return a new {@link SubtitleData} object + */ + public @NonNull SubtitleData build() { + return new SubtitleData(mTrackIndex, mStartTimeUs, mDurationUs, mData); + } + + /** + * Sets the info of subtitle data. + * + * @param trackIndex the ParcelFileDescriptor for the file to play + * @param startTimeUs the start time in microsecond for the subtile data + * @param durationUs the duration in microsecond for the subtile data + * @param data the data array for the subtile data. It should not be null. + * No data copying is made. + * @return the same Builder instance. + */ + public @NonNull Builder setSubtitleData( + int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) { + if (data == null) { + throw new IllegalArgumentException("null data is not allowed"); + } + mTrackIndex = trackIndex; + mStartTimeUs = startTimeUs; + mDurationUs = durationUs; + mData = data; + return this; + } + } } diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java index 97e6bfa170d8..bcc18ef92f94 100644 --- a/media/java/android/media/TimedMetaData.java +++ b/media/java/android/media/TimedMetaData.java @@ -16,8 +16,12 @@ package android.media; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; +import java.util.Arrays; + /** * Class that embodies one timed metadata access unit, including * @@ -50,7 +54,10 @@ public final class TimedMetaData { /** * @hide */ - public TimedMetaData(long timestampUs, byte[] metaData) { + public TimedMetaData(long timestampUs, @NonNull byte[] metaData) { + if (metaData == null) { + throw new IllegalArgumentException("null metaData is not allowed"); + } mTimestampUs = timestampUs; mMetaData = metaData; } @@ -83,4 +90,71 @@ public final class TimedMetaData { return true; } + + /** + * Builder class for {@link TimedMetaData} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link TimedMetaData}: + * + * <pre class="prettyprint"> + * TimedMetaData tmd = new TimedMetaData.Builder() + * .setTimedMetaData(timestamp, metaData) + * .build(); + * </pre> + * @hide + */ + @SystemApi + public static class Builder { + private long mTimestampUs; + private byte[] mMetaData = new byte[0]; + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + } + + /** + * Constructs a new Builder from a given {@link TimedMetaData} instance + * @param tmd the {@link TimedMetaData} object whose data will be reused + * in the new Builder. It should not be null. The metadata array is copied. + */ + public Builder(@NonNull TimedMetaData tmd) { + if (tmd == null) { + throw new IllegalArgumentException("null TimedMetaData is not allowed"); + } + mTimestampUs = tmd.mTimestampUs; + if (tmd.mMetaData != null) { + mMetaData = Arrays.copyOf(tmd.mMetaData, tmd.mMetaData.length); + } + } + + /** + * Combines all of the fields that have been set and return a new + * {@link TimedMetaData} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * + * @return a new {@link TimedMetaData} object + */ + public @NonNull TimedMetaData build() { + return new TimedMetaData(mTimestampUs, mMetaData); + } + + /** + * Sets the info of timed metadata. + * + * @param timestamp the timestamp in microsecond for the timed metadata + * @param metaData the metadata array for the timed metadata. No data copying is made. + * It should not be null. + * @return the same Builder instance. + */ + public @NonNull Builder setTimedMetaData(int timestamp, @NonNull byte[] metaData) { + if (metaData == null) { + throw new IllegalArgumentException("null metaData is not allowed"); + } + mTimestampUs = timestamp; + mMetaData = metaData; + return this; + } + } } diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java index 6a83dab14aa4..e39f53c9e19b 100644 --- a/media/java/android/media/UriDataSourceDesc.java +++ b/media/java/android/media/UriDataSourceDesc.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Map; /** - * @hide * Structure of data source descriptor for sources using URI. * * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, diff --git a/media/java/android/media/VideoSize.java b/media/java/android/media/VideoSize.java index 7e5cb1f457c8..19631e09853d 100644 --- a/media/java/android/media/VideoSize.java +++ b/media/java/android/media/VideoSize.java @@ -18,8 +18,6 @@ package android.media; /** * Immutable class for describing width and height dimensions. - * - * @hide */ public final class VideoSize { /** @@ -28,7 +26,7 @@ public final class VideoSize { * @param width The width of the video size * @param height The height of the video size */ - public VideoSize(int width, int height) { + VideoSize(int width, int height) { mWidth = width; mHeight = height; } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 8215779d2b13..d91cf8732674 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -96,9 +96,15 @@ public final class MediaSessionManager { * @return The binder object from the system * @hide */ + @SystemApi public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub, - @NonNull String tag, int userId) throws RemoteException { - return mService.createSession(mContext.getPackageName(), cbStub, tag, userId); + @NonNull String tag, int userId) { + try { + return mService.createSession(mContext.getPackageName(), cbStub, tag, userId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return null; } /** diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 456749279696..8b6009e749ce 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -1192,7 +1192,7 @@ static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArr } static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz, - jbyteArray uuidObj, jbyteArray drmSessionIdObj) + jlong srcId, jbyteArray uuidObj, jbyteArray drmSessionIdObj) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); if (mp == NULL) { @@ -1225,7 +1225,7 @@ static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz, return; } - status_t err = mp->prepareDrm(uuid.array(), drmSessionId); + status_t err = mp->prepareDrm(srcId, uuid.array(), drmSessionId); if (err != OK) { if (err == INVALID_OPERATION) { jniThrowException( @@ -1243,7 +1243,7 @@ static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz, } } -static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz) +static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz, jlong srcId) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { @@ -1251,7 +1251,7 @@ static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz) return; } - status_t err = mp->releaseDrm(); + status_t err = mp->releaseDrm(srcId); if (err != OK) { if (err == INVALID_OPERATION) { jniThrowException( @@ -1425,8 +1425,8 @@ static const JNINativeMethod gMethods[] = { {"native_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel}, {"native_attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect}, // Modular DRM - { "native_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm }, - { "native_releaseDrm", "()V", (void *)android_media_MediaPlayer2_releaseDrm }, + { "native_prepareDrm", "(J[B[B)V", (void *)android_media_MediaPlayer2_prepareDrm }, + { "native_releaseDrm", "(J)V", (void *)android_media_MediaPlayer2_releaseDrm }, // AudioRouting {"native_setPreferredDevice", "(Landroid/media/AudioDeviceInfo;)Z", (void *)android_media_MediaPlayer2_setPreferredDevice}, diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp index d8c5ac969128..96c9c1c85a60 100644 --- a/native/webview/plat_support/Android.bp +++ b/native/webview/plat_support/Android.bp @@ -23,6 +23,8 @@ cc_library_shared { srcs: [ "draw_gl_functor.cpp", + "draw_vk_functor.cpp", + "functor_utils.cpp", "jni_entry_point.cpp", "graphics_utils.cpp", "graphic_buffer_impl.cpp", @@ -36,6 +38,7 @@ cc_library_shared { "liblog", "libui", "libutils", + "libvulkan", ], // To remove warnings from skia header files diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp index e3e52b1ea1f1..be36b6742037 100644 --- a/native/webview/plat_support/draw_gl_functor.cpp +++ b/native/webview/plat_support/draw_gl_functor.cpp @@ -21,15 +21,13 @@ #include "draw_gl.h" -#include <errno.h> #include <jni.h> #include <private/hwui/DrawGlInfo.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/time.h> #include <utils/Functor.h> #include <utils/Log.h> +#include "functor_utils.h" + #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) #define COMPILE_ASSERT(expr, err) \ __unused static const char (err)[(expr) ? 1 : -1] = ""; @@ -98,27 +96,6 @@ class DrawGLFunctor : public Functor { intptr_t view_context_; }; -// Raise the file handle soft limit to the hard limit since gralloc buffers -// uses file handles. -void RaiseFileNumberLimit() { - static bool have_raised_limit = false; - if (have_raised_limit) - return; - - have_raised_limit = true; - struct rlimit limit_struct; - limit_struct.rlim_cur = 0; - limit_struct.rlim_max = 0; - if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) { - limit_struct.rlim_cur = limit_struct.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) { - ALOGE("setrlimit failed: %s", strerror(errno)); - } - } else { - ALOGE("getrlimit failed: %s", strerror(errno)); - } -} - jlong CreateGLFunctor(JNIEnv*, jclass, jlong view_context) { RaiseFileNumberLimit(); return reinterpret_cast<jlong>(new DrawGLFunctor(view_context)); diff --git a/native/webview/plat_support/draw_vk.h b/native/webview/plat_support/draw_vk.h new file mode 100644 index 000000000000..6b7d8d0b9118 --- /dev/null +++ b/native/webview/plat_support/draw_vk.h @@ -0,0 +1,125 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +//****************************************************************************** +// This is a copy of the coresponding android_webview/public/browser header. +// Any changes to the interface should be made there. +// +// The purpose of having the copy is twofold: +// - it removes the need to have Chromium sources present in the tree in order +// to build the plat_support library, +// - it captures API that the corresponding Android release supports. +//****************************************************************************** + +#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_ +#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_ + +#include <vulkan/vulkan.h> + +#ifdef __cplusplus +extern "C" { +#endif + +static const int kAwDrawVKInfoVersion = 1; + +// Holds the information required to trigger initialization of the Vulkan +// functor. +struct InitParams { + // All params are input + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + VkQueue queue; + uint32_t graphics_queue_index; + uint32_t instance_version; + const char* const* enabled_extension_names; + // Only one of device_features and device_features_2 should be non-null. + // If both are null then no features are enabled. + VkPhysicalDeviceFeatures* device_features; + VkPhysicalDeviceFeatures2* device_features_2; +}; + +// Holds the information required to trigger an Vulkan composite operation. +struct CompositeParams { + // Input: current width/height of destination surface. + int width; + int height; + + // Input: is the render target a FBO + bool is_layer; + + // Input: current transform matrix + float transform[16]; + + // Input WebView should do its main compositing draws into this. It cannot do + // anything that would require stopping the render pass. + VkCommandBuffer secondary_command_buffer; + + // Input: The main color attachment index where secondary_command_buffer will + // eventually be submitted. + uint32_t color_attachment_index; + + // Input: A render pass which will be compatible to the one which the + // secondary_command_buffer will be submitted into. + VkRenderPass compatible_render_pass; + + // Input: Format of the destination surface. + VkFormat format; + + // Input: Color space transfer params + float G; + float A; + float B; + float C; + float D; + float E; + float F; + + // Input: Color space transformation from linear RGB to D50-adapted XYZ + float matrix[9]; + + // Input: current clip rect + int clip_left; + int clip_top; + int clip_right; + int clip_bottom; +}; + +// Holds the information for the post-submission callback of main composite +// draw. +struct PostCompositeParams { + // Input: Fence for the composite command buffer to signal it has finished its + // work on the GPU. + int fd; +}; + +// Holds the information required to trigger an Vulkan operation. +struct AwDrawVKInfo { + int version; // The AwDrawVKInfo this struct was built with. + + // Input: tells the draw function what action to perform. + enum Mode { + kModeInit = 0, + kModeReInit = 1, + kModePreComposite = 2, + kModeComposite = 3, + kModePostComposite = 4, + kModeSync = 5, + } mode; + + // Input: The parameters for the functor being called + union ParamUnion { + struct InitParams init_params; + struct CompositeParams composite_params; + struct PostCompositeParams post_composite_params; + } info; +}; + +typedef void(AwDrawVKFunction)(long view_context, AwDrawVKInfo* draw_info); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_ diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp new file mode 100644 index 000000000000..1ba559d9afdf --- /dev/null +++ b/native/webview/plat_support/draw_vk_functor.cpp @@ -0,0 +1,141 @@ +/* + * 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. + */ + +// Provides a webviewchromium glue layer adapter from the internal Android +// Vulkan Functor data types into the types the chromium stack expects, and +// back. + +#define LOG_TAG "webviewchromium_plat_support" + +#include "draw_vk.h" + +#include <jni.h> +#include <private/hwui/DrawVkInfo.h> +#include <utils/Functor.h> +#include <utils/Log.h> + +#include "functor_utils.h" + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +namespace android { +namespace { + +AwDrawVKFunction* g_aw_drawvk_function = NULL; + +class DrawVKFunctor : public Functor { + public: + explicit DrawVKFunctor(jlong view_context) : view_context_(view_context) {} + ~DrawVKFunctor() override {} + + // Functor + status_t operator ()(int what, void* data) override { + using uirenderer::DrawVkInfo; + if (!g_aw_drawvk_function) { + ALOGE("Cannot draw: no DrawVK Function installed"); + return DrawVkInfo::kStatusDone; + } + + AwDrawVKInfo aw_info; + aw_info.version = kAwDrawVKInfoVersion; + switch (what) { + case DrawVkInfo::kModeComposite: { + aw_info.mode = AwDrawVKInfo::kModeComposite; + DrawVkInfo* vk_info = reinterpret_cast<DrawVkInfo*>(data); + + // Map across the input values. + CompositeParams& params = aw_info.info.composite_params; + params.width = vk_info->width; + params.height = vk_info->height; + params.is_layer = vk_info->isLayer; + for (size_t i = 0; i < 16; i++) { + params.transform[i] = vk_info->transform[i]; + } + params.secondary_command_buffer = vk_info->secondaryCommandBuffer; + params.color_attachment_index = vk_info->colorAttachmentIndex; + params.compatible_render_pass = vk_info->compatibleRenderPass; + params.format = vk_info->format; + params.G = vk_info->G; + params.A = vk_info->A; + params.B = vk_info->B; + params.C = vk_info->C; + params.D = vk_info->D; + params.E = vk_info->E; + params.F = vk_info->F; + for (size_t i = 0; i < 9; i++) { + params.matrix[i] = vk_info->matrix[i]; + } + params.clip_left = vk_info->clipLeft; + params.clip_top = vk_info->clipTop; + params.clip_right = vk_info->clipRight; + params.clip_bottom = vk_info->clipBottom; + + break; + } + case DrawVkInfo::kModePostComposite: + break; + case DrawVkInfo::kModeSync: + aw_info.mode = AwDrawVKInfo::kModeSync; + break; + default: + ALOGE("Unexpected DrawVKInfo type %d", what); + return DrawVkInfo::kStatusDone; + } + + // Invoke the DrawVK method. + g_aw_drawvk_function(view_context_, &aw_info); + + return DrawVkInfo::kStatusDone; + } + + private: + intptr_t view_context_; +}; + +jlong CreateVKFunctor(JNIEnv*, jclass, jlong view_context) { + RaiseFileNumberLimit(); + return reinterpret_cast<jlong>(new DrawVKFunctor(view_context)); +} + +void DestroyVKFunctor(JNIEnv*, jclass, jlong functor) { + delete reinterpret_cast<DrawVKFunctor*>(functor); +} + +void SetChromiumAwDrawVKFunction(JNIEnv*, jclass, jlong draw_function) { + g_aw_drawvk_function = reinterpret_cast<AwDrawVKFunction*>(draw_function); +} + +const char kClassName[] = "com/android/webview/chromium/DrawVKFunctor"; +const JNINativeMethod kJniMethods[] = { + { "nativeCreateVKFunctor", "(J)J", + reinterpret_cast<void*>(CreateVKFunctor) }, + { "nativeDestroyVKFunctor", "(J)V", + reinterpret_cast<void*>(DestroyVKFunctor) }, + { "nativeSetChromiumAwDrawVKFunction", "(J)V", + reinterpret_cast<void*>(SetChromiumAwDrawVKFunction) }, +}; + +} // namespace + +void RegisterDrawVKFunctor(JNIEnv* env) { + jclass clazz = env->FindClass(kClassName); + LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName); + + int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods)); + LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res); +} + +} // namespace android diff --git a/native/webview/plat_support/functor_utils.cpp b/native/webview/plat_support/functor_utils.cpp new file mode 100644 index 000000000000..235762dc2679 --- /dev/null +++ b/native/webview/plat_support/functor_utils.cpp @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#include "functor_utils.h" + +#include <errno.h> +#include <string.h> +#include <sys/resource.h> +#include <utils/Log.h> + +namespace android { + +void RaiseFileNumberLimit() { + static bool have_raised_limit = false; + if (have_raised_limit) + return; + + have_raised_limit = true; + struct rlimit limit_struct; + limit_struct.rlim_cur = 0; + limit_struct.rlim_max = 0; + if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) { + limit_struct.rlim_cur = limit_struct.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) { + ALOGE("setrlimit failed: %s", strerror(errno)); + } + } else { + ALOGE("getrlimit failed: %s", strerror(errno)); + } +} + +} // namespace android diff --git a/native/webview/plat_support/functor_utils.h b/native/webview/plat_support/functor_utils.h new file mode 100644 index 000000000000..76c0bb67d275 --- /dev/null +++ b/native/webview/plat_support/functor_utils.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android { + +// Raise the file handle soft limit to the hard limit since gralloc buffers +// uses file handles. +void RaiseFileNumberLimit(); + +} // namespace android diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml index 74f38d4f9946..74f38d4f9946 100644 --- a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml +++ b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml index 0f28297f0214..0f28297f0214 100644 --- a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml +++ b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml index e6757d2862b1..e6757d2862b1 100644 --- a/packages/SystemUI/res/anim/car_arrow_fade_out.xml +++ b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml index ed637a7adcbf..6f12338d8db8 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml index 227c981cb72a..9f8c12ef16b5 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml index 5901ff41735c..adc1f720e91b 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml index 41cbe4b104c3..dec5c05dbce8 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml index 341e7e0cdfe9..986a9cb06459 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml index 6ae74131584d..80b38b388aed 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml index 06ac9e359b57..721376cae960 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml index 4baefb83a4ef..246099ef7b35 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml index 2d0deb95da59..9a1c642363d0 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml index 3315220a60ef..1414b6688c8f 100644 --- a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml +++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml @@ -1,4 +1,4 @@ -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/drawable/car_add_circle_round.xml b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml index 13c7dd12f940..13c7dd12f940 100644 --- a/packages/SystemUI/res/drawable/car_add_circle_round.xml +++ b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml diff --git a/packages/SystemUI/res/drawable/car_ic_add_white.xml b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml index d6818607ec08..d6818607ec08 100644 --- a/packages/SystemUI/res/drawable/car_ic_add_white.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml index d400ed8c7707..cfacbf98354f 100644 --- a/packages/SystemUI/res/drawable/car_ic_arrow.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml @@ -1,5 +1,5 @@ <!-- - ~ Copyright (C) 2017 The Android Open Source Project + ~ Copyright (C) 2018 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml index 33a512e31675..81e7262c7cea 100644 --- a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml @@ -1,5 +1,5 @@ <!-- - ~ Copyright (C) 2015 The Android Open Source Project + ~ Copyright (C) 2018 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml index bdc44b38a176..bdc44b38a176 100644 --- a/packages/SystemUI/res/drawable/car_ic_hvac.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml index 3709aa546766..3709aa546766 100644 --- a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml index 328efa08abdd..eb501e5c26eb 100644 --- a/packages/SystemUI/res/drawable/car_ic_navigation.xml +++ b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml @@ -14,15 +14,14 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="32dp" - android:height="37dp" - android:viewportWidth="32.0" - android:viewportHeight="37.0"> - <path - android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z" - android:strokeColor="#00000000" - android:fillType="evenOdd" - android:fillColor="@color/car_grey_50" - android:strokeWidth="1"/> -</vector>
\ No newline at end of file + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners + android:bottomLeftRadius="@dimen/car_radius_3" + android:topLeftRadius="0dp" + android:bottomRightRadius="@dimen/car_radius_3" + android:topRightRadius="0dp" + /> +</shape> diff --git a/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml new file mode 100644 index 000000000000..34578fe252d4 --- /dev/null +++ b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="17dp" + android:height="17dp" + android:viewportWidth="18.0" + android:viewportHeight="18.0"> + <group + android:translateY="0.5" + android:translateX="0.5" > + <path + android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z" + android:fillColor="#FFFFFF"/> + </group> +</vector> diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml index f5a91b5ded32..e1e389d7f472 100644 --- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml +++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml @@ -1,3 +1,19 @@ +<?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 + --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml index ad8604935628..8e7ebad7d7ec 100644 --- a/packages/SystemUI/res/layout/car_facet_button.xml +++ b/packages/CarSystemUI/res/layout/car_facet_button.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2017, The Android Open Source Project +** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml index ee8d357a0b80..1d6728689933 100644 --- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml +++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (C) 2015 The Android Open Source Project + Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml index c9f51486e983..6cd70d62b4f7 100644 --- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml +++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2015 The Android Open Source Project + Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml index 02be45788761..141b28a9ae28 100644 --- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2016, The Android Open Source Project +** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml index 708f5955f306..708f5955f306 100644 --- a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml index 4062eb8068fa..6d8cca990fa1 100644 --- a/packages/SystemUI/res/layout/car_navigation_button.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2016, The Android Open Source Project +** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml index 3afd4ea33039..6f19cfcfa345 100644 --- a/packages/SystemUI/res/layout/car_qs_footer.xml +++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml index e7413de342fa..dfa48c30b0c8 100644 --- a/packages/SystemUI/res/layout/car_qs_panel.xml +++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml index 91ba02622e9a..141b28a9ae28 100644 --- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2016, The Android Open Source Project +** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -58,7 +58,6 @@ android:paddingTop="30dp" android:paddingBottom="30dp" /> - </LinearLayout> <LinearLayout @@ -79,7 +78,6 @@ android:alpha="0.7" /> - <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:textAppearance="@style/TextAppearance.StatusBar.Clock" diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml index 708f5955f306..708f5955f306 100644 --- a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml index 49bfb250acab..2f720f5aba65 100644 --- a/packages/SystemUI/res/values/colors_car.xml +++ b/packages/CarSystemUI/res/values/colors_car.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* - * Copyright 2017, The Android Open Source Project + * Copyright 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml index afbe17632346..c027f81eb8bd 100644 --- a/packages/SystemUI/res/values/dimens_car.xml +++ b/packages/CarSystemUI/res/values/dimens_car.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - * Copyright (c) 2016, The Android Open Source Project + * Copyright (c) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/values/ids_car.xml b/packages/CarSystemUI/res/values/ids_car.xml index 27ed2e250d9f..27ed2e250d9f 100644 --- a/packages/SystemUI/res/values/ids_car.xml +++ b/packages/CarSystemUI/res/values/ids_car.xml diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml index fc3623cb69fb..472c957ab568 100644 --- a/packages/SystemUI/res/values/integers_car.xml +++ b/packages/CarSystemUI/res/values/integers_car.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (c) 2017, The Android Open Source Project + Copyright (c) 2018, The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml index 2890cf275b37..83e91c57ccc3 100644 --- a/packages/SystemUI/res/values/strings_car.xml +++ b/packages/CarSystemUI/res/values/strings_car.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /** - * Copyright (c) 2016, The Android Open Source Project + * Copyright (c) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java index dfe57044e4e0..f57f26db118c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java @@ -28,6 +28,8 @@ import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.volume.CarVolumeDialogComponent; +import com.android.systemui.volume.VolumeDialogComponent; /** * Class factory to provide car specific SystemUI components. @@ -39,6 +41,10 @@ public class CarSystemUIFactory extends SystemUIFactory { return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils); } + public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) { + return new CarVolumeDialogComponent(systemUi, context); + } + @Override public void injectDependencies(ArrayMap<Object, DependencyProvider> providers, Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index 50fefe948aa8..0563418e5fb9 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,8 +11,9 @@ * 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 + * limitations under the License. */ + package com.android.systemui.car; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java index 0389030a17b2..b74f1998bf9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java +++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java @@ -1,16 +1,19 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at + * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package com.android.systemui.qs.car; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java index 3e82c54dc811..41c37d3bbc19 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java +++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java @@ -1,16 +1,19 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at + * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package com.android.systemui.qs.car; import android.animation.Animator; @@ -223,7 +226,7 @@ public class CarQSFragment extends Fragment implements QS { private void animateHeightChange(boolean opening) { // Animation in progress; cancel it to avoid contention. - if (mAnimatorSet != null){ + if (mAnimatorSet != null) { mAnimatorSet.cancel(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java index 083a7471c3c1..d5dd3c3eaca0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java +++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java @@ -1,16 +1,19 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at + * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package com.android.systemui.qs.car; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java index fc39648b7ac0..58f80a4ed968 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java @@ -58,28 +58,31 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo private final Context mContext; private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); - private BluetoothHeadsetClient mBluetoothHeadsetClient; - private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>(); + private BluetoothHeadsetClient mBluetoothHeadsetClient; + private final ServiceListener mHfpServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET_CLIENT) { + mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy; + } + } + @Override + public void onServiceDisconnected(int profile) { + if (profile == BluetoothProfile.HEADSET_CLIENT) { + mBluetoothHeadsetClient = null; + } + } + }; private int mLevel; - - /** - * An interface indicating the container of a View that will display what the information - * in the {@link CarBatteryController}. - */ - public interface BatteryViewHandler { - void hideBatteryView(); - void showBatteryView(); - } - private BatteryViewHandler mBatteryViewHandler; public CarBatteryController(Context context) { mContext = context; if (mAdapter == null) { - return; + return; } mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener, @@ -159,7 +162,7 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo } BluetoothDevice device = - (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE); + (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE); updateBatteryIcon(device, newState); } } @@ -261,20 +264,14 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo } } - private final ServiceListener mHfpServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (profile == BluetoothProfile.HEADSET_CLIENT) { - mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy; - } - } + /** + * An interface indicating the container of a View that will display what the information + * in the {@link CarBatteryController}. + */ + public interface BatteryViewHandler { + void hideBatteryView(); - @Override - public void onServiceDisconnected(int profile) { - if (profile == BluetoothProfile.HEADSET_CLIENT) { - mBluetoothHeadsetClient = null; - } - } - }; + void showBatteryView(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java index 20986adbfe9f..56db242f1eb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.car; import android.app.ActivityManager; @@ -34,7 +50,6 @@ public class CarFacetButtonController { * Add facet button to this controller. The expected use is for the facet button * to get a reference to this controller via {@link com.android.systemui.Dependency} * and self add. - * @param facetButton */ public void addFacetButton(CarFacetButton facetButton) { String[] categories = facetButton.getCategories(); @@ -70,15 +85,16 @@ public class CarFacetButtonController { * They will then be compared with the supplied StackInfo list. * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used * for consideration if it has the same displayId as the CarFacetButtons. - * @param taskInfo of the currently running application + * + * @param stackInfoList of the currently running application */ public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) { int displayId = getDisplayId(); ActivityManager.StackInfo validStackInfo = null; - for (ActivityManager.StackInfo stackInfo :stackInfoList) { + for (ActivityManager.StackInfo stackInfo : stackInfoList) { // If the display id is unknown or it matches the stack, it's valid for use - if ((displayId == -1 || displayId == stackInfo.displayId) && - stackInfo.topActivity != null) { + if ((displayId == -1 || displayId == stackInfo.displayId) + && stackInfo.topActivity != null) { validStackInfo = stackInfo; break; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index 81f7846b357d..81f7846b357d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index 084c136fc555..e640baa4327a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.car; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 4bff5ba3de7f..2d90f8f0afd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,8 @@ import com.android.systemui.R; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.car.CarQSFragment; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.StatusBarState; @@ -252,6 +254,11 @@ public class CarStatusBar extends StatusBar implements addTemperatureViewToController(mStatusBarWindow); } + @Override + protected QS createDefaultQSFragment() { + return new CarQSFragment(); + } + private BatteryController createBatteryController() { mCarBatteryController = new CarBatteryController(mContext); mCarBatteryController.addBatteryViewHandler(this); @@ -549,7 +556,7 @@ public class CarStatusBar extends StatusBar implements */ public void dismissKeyguard() { executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */, - true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); + true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java index d0f06296545b..8c6b9b0107a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.car; import android.content.Context; @@ -16,13 +32,13 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage ViewMediatorCallback callback, LockPatternUtils lockPatternUtils) { super(context, callback, lockPatternUtils); - mShouldHideNavBar =context.getResources() + mShouldHideNavBar = context.getResources() .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown); } @Override protected void updateNavigationBarVisibility(boolean navBarVisible) { - if(!mShouldHideNavBar) { + if (!mShouldHideNavBar) { return; } CarStatusBar statusBar = (CarStatusBar) mStatusBar; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java index f2923f7df248..3288927bf730 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.car; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; @@ -29,7 +45,7 @@ import com.android.systemui.statusbar.policy.BluetoothController; */ public class ConnectedDeviceSignalController extends BroadcastReceiver implements BluetoothController.Callback { - private final static String TAG = "DeviceSignalCtlr"; + private static final String TAG = "DeviceSignalCtlr"; /** * The value that indicates if a network is unavailable. This value is according ot the @@ -70,6 +86,21 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement private final SignalDrawable mSignalDrawable; private BluetoothHeadsetClient mBluetoothHeadsetClient; + private final ServiceListener mHfpServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET_CLIENT) { + mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy; + } + } + + @Override + public void onServiceDisconnected(int profile) { + if (profile == BluetoothProfile.HEADSET_CLIENT) { + mBluetoothHeadsetClient = null; + } + } + }; public ConnectedDeviceSignalController(Context context, View signalsView) { mContext = context; @@ -87,7 +118,7 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor)); if (mAdapter == null) { - return; + return; } mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener, @@ -236,20 +267,4 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement mSignalsView.setVisibility(View.GONE); } } - - private final ServiceListener mHfpServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (profile == BluetoothProfile.HEADSET_CLIENT) { - mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy; - } - } - - @Override - public void onServiceDisconnected(int profile) { - if (profile == BluetoothProfile.HEADSET_CLIENT) { - mBluetoothHeadsetClient = null; - } - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java index 47941bf22941..730c3e3440ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java @@ -124,4 +124,4 @@ public class DrivingStateHelper { Log.d(TAG, message); } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 2ebf5eb39bf6..23fe5944573e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.systemui.statusbar.car; @@ -44,7 +44,7 @@ public class FullscreenUserSwitcher { // Initialize user grid. mUserGridView = container.findViewById(R.id.user_grid); GridLayoutManager layoutManager = new GridLayoutManager(context, - context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col)); + context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col)); mUserGridView.getRecyclerView().setLayoutManager(layoutManager); mUserGridView.buildAdapter(); mUserGridView.setUserSelectionListener(this::onUserSelected); @@ -54,7 +54,7 @@ public class FullscreenUserSwitcher { hide(); mShortAnimDuration = container.getResources() - .getInteger(android.R.integer.config_shortAnimTime); + .getInteger(android.R.integer.config_shortAnimTime); } /** @@ -108,4 +108,4 @@ public class FullscreenUserSwitcher { }); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java index 0c91cba43390..0c91cba43390 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java index d802ed8d6e3a..fb2b57b6d490 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.systemui.statusbar.car; @@ -210,7 +210,7 @@ public class UserGridRecyclerView extends PagedListView implements public void onBindViewHolder(UserAdapterViewHolder holder, int position) { UserRecord userRecord = mUsers.get(position); RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes, - getUserRecordIcon(userRecord)); + getUserRecordIcon(userRecord)); circleIcon.setCircular(true); holder.mUserAvatarImageView.setImageDrawable(circleIcon); holder.mUserNameTextView.setText(userRecord.mInfo.name); @@ -254,13 +254,13 @@ public class UserGridRecyclerView extends PagedListView implements private void showMaxUserLimitReachedDialog() { AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert) - .setTitle(R.string.user_limit_reached_title) - .setMessage(getResources().getQuantityString( - R.plurals.user_limit_reached_message, - mCarUserManagerHelper.getMaxSupportedRealUsers(), - mCarUserManagerHelper.getMaxSupportedRealUsers())) - .setPositiveButton(android.R.string.ok, null) - .create(); + .setTitle(R.string.user_limit_reached_title) + .setMessage(getResources().getQuantityString( + R.plurals.user_limit_reached_message, + mCarUserManagerHelper.getMaxSupportedRealUsers(), + mCarUserManagerHelper.getMaxSupportedRealUsers())) + .setPositiveButton(android.R.string.ok, null) + .create(); // Sets window flags for the SysUI dialog SystemUIDialog.applyFlags(maxUsersDialog); maxUsersDialog.show(); @@ -268,17 +268,17 @@ public class UserGridRecyclerView extends PagedListView implements private void showConfirmAddUserDialog() { String message = mRes.getString(R.string.user_add_user_message_setup) - .concat(System.getProperty("line.separator")) - .concat(System.getProperty("line.separator")) - .concat(mRes.getString(R.string.user_add_user_message_update)); + .concat(System.getProperty("line.separator")) + .concat(System.getProperty("line.separator")) + .concat(mRes.getString(R.string.user_add_user_message_update)); AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert) - .setTitle(R.string.user_add_user_title) - .setMessage(message) - .setNegativeButton(android.R.string.cancel, this) - .setPositiveButton(android.R.string.ok, this) - .setOnCancelListener(this) - .create(); + .setTitle(R.string.user_add_user_title) + .setMessage(message) + .setNegativeButton(android.R.string.cancel, this) + .setPositiveButton(android.R.string.ok, this) + .setOnCancelListener(this) + .create(); // Sets window flags for the SysUI dialog SystemUIDialog.applyFlags(addUserDialog); addUserDialog.show(); @@ -298,7 +298,7 @@ public class UserGridRecyclerView extends PagedListView implements if (userRecord.mIsAddUser) { return UserIcons.convertToBitmap(mContext - .getDrawable(R.drawable.car_add_circle_round)); + .getDrawable(R.drawable.car_add_circle_round)); } return mCarUserManagerHelper.getUserIcon(userRecord.mInfo); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java index 6c924e3c2c16..aec31ee4bfdd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -41,31 +41,58 @@ import java.util.Objects; public class HvacController { public static final String TAG = "HvacController"; - public final static int BIND_TO_HVAC_RETRY_DELAY = 5000; + public static final int BIND_TO_HVAC_RETRY_DELAY = 5000; private Context mContext; private Handler mHandler; private Car mCar; private CarHvacManager mHvacManager; private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>(); - - public HvacController(Context context) { - mContext = context; - } - /** - * Create connection to the Car service. Note: call backs from the Car service - * ({@link CarHvacManager}) will happen on the same thread this method was called from. + * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to + * match. */ - public void connectToCarService() { - mHandler = new Handler(); - mCar = Car.createCar(mContext, mServiceConnection, mHandler); - if (mCar != null) { - // note: this connect call handles the retries - mCar.connect(); + private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() { + @Override + public void onChangeEvent(final CarPropertyValue val) { + try { + int areaId = val.getAreaId(); + int propertyId = val.getPropertyId(); + List<TemperatureView> temperatureViews = mTempComponents.get( + new HvacKey(propertyId, areaId)); + if (temperatureViews != null && !temperatureViews.isEmpty()) { + float value = (float) val.getValue(); + for (TemperatureView tempView : temperatureViews) { + tempView.setTemp(value); + } + } // else the data is not of interest + } catch (Exception e) { + // catch all so we don't take down the sysui if a new data type is + // introduced. + Log.e(TAG, "Failed handling hvac change event", e); + } } - } + @Override + public void onErrorEvent(final int propertyId, final int zone) { + Log.d(TAG, "HVAC error event, propertyId: " + propertyId + + " zone: " + zone); + } + }; + /** + * If the connection to car service goes away then restart it. + */ + private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.d(TAG, "Death of HVAC triggering a restart"); + if (mCar != null) { + mCar.disconnect(); + } + destroyHvacManager(); + mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY); + } + }; /** * Registers callbacks and initializes components upon connection. */ @@ -88,6 +115,23 @@ public class HvacController { } }; + public HvacController(Context context) { + mContext = context; + } + + /** + * Create connection to the Car service. Note: call backs from the Car service + * ({@link CarHvacManager}) will happen on the same thread this method was called from. + */ + public void connectToCarService() { + mHandler = new Handler(); + mCar = Car.createCar(mContext, mServiceConnection, mHandler); + if (mCar != null) { + // note: this connect call handles the retries + mCar.connect(); + } + } + private void destroyHvacManager() { if (mHvacManager != null) { mHvacManager.unregisterCallback(mHardwareCallback); @@ -96,23 +140,7 @@ public class HvacController { } /** - * If the connection to car service goes away then restart it. - */ - private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() { - @Override - public void binderDied() { - Log.d(TAG, "Death of HVAC triggering a restart"); - if (mCar != null) { - mCar.disconnect(); - } - destroyHvacManager(); - mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY); - } - }; - - /** * Add component to list and initialize it if the connection is up. - * @param temperatureView */ public void addHvacTextView(TemperatureView temperatureView) { @@ -136,7 +164,6 @@ public class HvacController { } } - private void initComponent(TemperatureView view) { int id = view.getPropertyId(); int zone = view.getAreaId(); @@ -153,38 +180,6 @@ public class HvacController { } /** - * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to - * match. - */ - private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() { - @Override - public void onChangeEvent(final CarPropertyValue val) { - try { - int areaId = val.getAreaId(); - int propertyId = val.getPropertyId(); - List<TemperatureView> temperatureViews = mTempComponents.get( - new HvacKey(propertyId, areaId)); - if (temperatureViews != null && !temperatureViews.isEmpty()) { - float value = (float) val.getValue(); - for (TemperatureView tempView : temperatureViews) { - tempView.setTemp(value); - } - } // else the data is not of interest - } catch (Exception e) { - // catch all so we don't take down the sysui if a new data type is - // introduced. - Log.e(TAG, "Failed handling hvac change event", e); - } - } - - @Override - public void onErrorEvent(final int propertyId, final int zone) { - Log.d(TAG, "HVAC error event, propertyId: " + propertyId + - " zone: " + zone); - } - }; - - /** * Removes all registered components. This is useful if you need to rebuild the UI since * components self register. */ @@ -200,7 +195,7 @@ public class HvacController { int mPropertyId; int mAreaId; - public HvacKey(int propertyId, int areaId) { + private HvacKey(int propertyId, int areaId) { mPropertyId = propertyId; mAreaId = areaId; } @@ -210,8 +205,8 @@ public class HvacController { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HvacKey hvacKey = (HvacKey) o; - return mPropertyId == hvacKey.mPropertyId && - mAreaId == hvacKey.mAreaId; + return mPropertyId == hvacKey.mPropertyId + && mAreaId == hvacKey.mAreaId; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java index 4d8ce4348319..507c60f87b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java @@ -40,7 +40,7 @@ public class TemperatureTextView extends TextView implements TemperatureView { public TemperatureTextView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView); - mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1); + mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1); mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1); String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat); mTempFormat = (format == null) ? "%.1f\u00B0" : format; @@ -48,6 +48,7 @@ public class TemperatureTextView extends TextView implements TemperatureView { /** * Formats the float for display + * * @param temp - The current temp or NaN */ @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java index 7651356f98a3..7651356f98a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java new file mode 100644 index 000000000000..71cc19b63ac1 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume; + +import android.content.Context; + +import com.android.systemui.SystemUI; +import com.android.systemui.plugins.VolumeDialog; + +/** + * Allows for adding car specific dialog when the volume dialog is created. + */ +public class CarVolumeDialogComponent extends VolumeDialogComponent { + + public CarVolumeDialogComponent(SystemUI sysui, Context context) { + super(sysui, context); + } + + protected VolumeDialog createDefault() { + return new CarVolumeDialogImpl(mContext); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java new file mode 100644 index 000000000000..12df263ffede --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.annotation.DrawableRes; +import android.annotation.Nullable; +import android.app.Dialog; +import android.app.KeyguardManager; +import android.car.Car; +import android.car.CarNotConnectedException; +import android.car.media.CarAudioManager; +import android.car.media.ICarVolumeCallback; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.ServiceConnection; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.media.AudioManager; +import android.os.Debug; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.util.Xml; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; + +import androidx.car.widget.ListItem; +import androidx.car.widget.ListItemAdapter; +import androidx.car.widget.ListItemAdapter.BackgroundStyle; +import androidx.car.widget.ListItemProvider.ListProvider; +import androidx.car.widget.PagedListView; +import androidx.car.widget.SeekbarListItem; + +import com.android.systemui.R; +import com.android.systemui.plugins.VolumeDialog; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Car version of the volume dialog. + * + * Methods ending in "H" must be called on the (ui) handler. + */ +public class CarVolumeDialogImpl implements VolumeDialog { + + private static final String TAG = Util.logTag(CarVolumeDialogImpl.class); + + private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; + private static final String XML_TAG_VOLUME_ITEM = "item"; + private static final int HOVERING_TIMEOUT = 16000; + private static final int NORMAL_TIMEOUT = 3000; + private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250; + private static final int DISMISS_DELAY_IN_MILLIS = 50; + private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100; + + private final Context mContext; + private final H mHandler = new H(); + // All the volume items. + private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>(); + // Available volume items in car audio manager. + private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>(); + // Volume items in the PagedListView. + private final List<ListItem> mVolumeLineItems = new ArrayList<>(); + private final KeyguardManager mKeyguard; + private Window mWindow; + private CustomDialog mDialog; + private PagedListView mListView; + private ListItemAdapter mPagedListAdapter; + private Car mCar; + private CarAudioManager mCarAudioManager; + private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() { + @Override + public void onGroupVolumeChanged(int groupId, int flags) { + VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); + int value = getSeekbarValue(mCarAudioManager, groupId); + // Do not update the progress if it is the same as before. When car audio manager sets + // its group volume caused by the seekbar progress changed, it also triggers this + // callback. Updating the seekbar at the same time could block the continuous seeking. + if (value != volumeItem.progress) { + volumeItem.listItem.setProgress(value); + volumeItem.progress = value; + } + if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { + mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget(); + } + } + + @Override + public void onMasterMuteChanged(int flags) { + // ignored + } + }; + private boolean mHovering; + private boolean mShowing; + private boolean mExpanded; + private final ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + try { + mExpanded = false; + mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); + int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); + // Populates volume slider items from volume groups to UI. + for (int groupId = 0; groupId < volumeGroupCount; groupId++) { + VolumeItem volumeItem = getVolumeItemForUsages( + mCarAudioManager.getUsagesForVolumeGroupId(groupId)); + mAvailableVolumeItems.add(volumeItem); + // The first one is the default item. + if (groupId == 0) { + volumeItem.defaultItem = true; + addSeekbarListItem(volumeItem, groupId, + R.drawable.car_ic_keyboard_arrow_down, + new ExpandIconListener()); + } + } + + // If list is already initiated, update its content. + if (mPagedListAdapter != null) { + mPagedListAdapter.notifyDataSetChanged(); + } + mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder()); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); + } + } + + /** + * This does not get called when service is properly disconnected. + * So we need to also handle cleanups in destroy(). + */ + @Override + public void onServiceDisconnected(ComponentName name) { + cleanupAudioManager(); + } + }; + + public CarVolumeDialogImpl(Context context) { + mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); + mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mCar = Car.createCar(mContext, mServiceConnection); + } + + private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { + try { + return carAudioManager.getGroupVolume(volumeGroupId); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); + } + return 0; + } + + private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { + try { + return carAudioManager.getGroupMaxVolume(volumeGroupId); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); + } + return 0; + } + + /** + * Build the volume window and connect to the CarService which registers with car audio + * manager. + */ + @Override + public void init(int windowType, Callback callback) { + initDialog(); + + mCar.connect(); + } + + @Override + public void destroy() { + mHandler.removeCallbacksAndMessages(null); + + cleanupAudioManager(); + // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup + // audio manager beforehand. + mCar.disconnect(); + } + + private void initDialog() { + loadAudioUsageItems(); + mVolumeLineItems.clear(); + mDialog = new CustomDialog(mContext); + + mHovering = false; + mShowing = false; + mExpanded = false; + mWindow = mDialog.getWindow(); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); + mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast); + final WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.format = PixelFormat.TRANSLUCENT; + lp.setTitle(VolumeDialogImpl.class.getSimpleName()); + lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + lp.windowAnimations = -1; + mWindow.setAttributes(lp); + mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + mDialog.setCanceledOnTouchOutside(true); + mDialog.setContentView(R.layout.car_volume_dialog); + mDialog.setOnShowListener(dialog -> { + mListView.setTranslationY(-mListView.getHeight()); + mListView.setAlpha(0); + mListView.animate() + .alpha(1) + .translationY(0) + .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) + .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) + .start(); + }); + mListView = (PagedListView) mWindow.findViewById(R.id.volume_list); + mListView.setOnHoverListener((v, event) -> { + int action = event.getActionMasked(); + mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) + || (action == MotionEvent.ACTION_HOVER_MOVE); + rescheduleTimeoutH(); + return true; + }); + + mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems), + BackgroundStyle.PANEL); + mListView.setAdapter(mPagedListAdapter); + mListView.setMaxPages(PagedListView.UNLIMITED_PAGES); + } + + + private void showH(int reason) { + if (D.BUG) { + Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]); + } + + mHandler.removeMessages(H.SHOW); + mHandler.removeMessages(H.DISMISS); + rescheduleTimeoutH(); + // Refresh the data set before showing. + mPagedListAdapter.notifyDataSetChanged(); + if (mShowing) { + return; + } + mShowing = true; + + mDialog.show(); + Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); + } + + private void rescheduleTimeoutH() { + mHandler.removeMessages(H.DISMISS); + final int timeout = computeTimeoutH(); + mHandler.sendMessageDelayed(mHandler + .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout); + + if (D.BUG) { + Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); + } + } + + private int computeTimeoutH() { + return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT; + } + + private void dismissH(int reason) { + if (D.BUG) { + Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]); + } + + mHandler.removeMessages(H.DISMISS); + mHandler.removeMessages(H.SHOW); + if (!mShowing) { + return; + } + + mListView.animate().cancel(); + + mListView.setTranslationY(0); + mListView.setAlpha(1); + mListView.animate() + .alpha(0) + .translationY(-mListView.getHeight()) + .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) + .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) + .withEndAction(() -> mHandler.postDelayed(() -> { + if (D.BUG) { + Log.d(TAG, "mDialog.dismiss()"); + } + mDialog.dismiss(); + mShowing = false; + }, DISMISS_DELAY_IN_MILLIS)) + .start(); + + Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason); + } + + private void loadAudioUsageItems() { + try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) { + AttributeSet attrs = Xml.asAttributeSet(parser); + int type; + // Traverse to the first start tag + while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT + && type != XmlResourceParser.START_TAG) { + // Do Nothing (moving parser to start element) + } + + if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) { + throw new RuntimeException("Meta-data does not start with carVolumeItems tag"); + } + int outerDepth = parser.getDepth(); + int rank = 0; + while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT + && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlResourceParser.END_TAG) { + continue; + } + if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { + TypedArray item = mContext.getResources().obtainAttributes( + attrs, R.styleable.carVolumeItems_item); + int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); + if (usage >= 0) { + VolumeItem volumeItem = new VolumeItem(); + volumeItem.rank = rank; + volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, + 0); + mVolumeItems.put(usage, volumeItem); + rank++; + } + item.recycle(); + } + } + } catch (XmlPullParserException | IOException e) { + Log.e(TAG, "Error parsing volume groups configuration", e); + } + } + + private VolumeItem getVolumeItemForUsages(int[] usages) { + int rank = Integer.MAX_VALUE; + VolumeItem result = null; + for (int usage : usages) { + VolumeItem volumeItem = mVolumeItems.get(usage); + if (volumeItem.rank < rank) { + rank = volumeItem.rank; + result = volumeItem; + } + } + return result; + } + + private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, + int volumeGroupId, + int supplementalIconId, + @Nullable View.OnClickListener supplementalIconOnClickListener) { + SeekbarListItem listItem = new SeekbarListItem(mContext); + listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId)); + int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); + int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); + listItem.setProgress(progress); + listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl + .VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); + Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); + primaryIcon.mutate().setTint(color); + listItem.setPrimaryActionIcon(primaryIcon); + if (supplementalIconId != 0) { + Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); + supplementalIcon.mutate().setTint(color); + listItem.setSupplementalIcon(supplementalIcon, true); + listItem.setSupplementalIconListener(supplementalIconOnClickListener); + } else { + listItem.setSupplementalEmptyIcon(true); + listItem.setSupplementalIconListener(null); + } + + mVolumeLineItems.add(listItem); + volumeItem.listItem = listItem; + volumeItem.progress = progress; + return listItem; + } + + private VolumeItem findVolumeItem(SeekbarListItem targetItem) { + for (int i = 0; i < mVolumeItems.size(); ++i) { + VolumeItem volumeItem = mVolumeItems.valueAt(i); + if (volumeItem.listItem == targetItem) { + return volumeItem; + } + } + return null; + } + + private void cleanupAudioManager() { + try { + mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder()); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); + } + mVolumeLineItems.clear(); + mCarAudioManager = null; + } + + /** + * Wrapper class which contains information of each volume group. + */ + private static class VolumeItem { + + private int rank; + private boolean defaultItem = false; + private @DrawableRes int icon; + private SeekbarListItem listItem; + private int progress; + } + + private final class H extends Handler { + + private static final int SHOW = 1; + private static final int DISMISS = 2; + + private H() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SHOW: + showH(msg.arg1); + break; + case DISMISS: + dismissH(msg.arg1); + break; + default: + } + } + } + + private final class CustomDialog extends Dialog implements DialogInterface { + + private CustomDialog(Context context) { + super(context, com.android.systemui.R.style.qs_theme); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + rescheduleTimeoutH(); + return super.dispatchTouchEvent(ev); + } + + @Override + protected void onStart() { + super.setCanceledOnTouchOutside(true); + super.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (isShowing()) { + if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { + mHandler.obtainMessage( + H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget(); + return true; + } + } + return false; + } + } + + private final class ExpandIconListener implements View.OnClickListener { + + @Override + public void onClick(final View v) { + mExpanded = !mExpanded; + Animator inAnimator; + if (mExpanded) { + for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { + // Adding the items which are not coming from the default item. + VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); + if (volumeItem.defaultItem) { + // Set progress here due to the progress of seekbar may not be updated. + volumeItem.listItem.setProgress(volumeItem.progress); + } else { + addSeekbarListItem(volumeItem, groupId, 0, null); + } + } + inAnimator = AnimatorInflater.loadAnimator( + mContext, R.anim.car_arrow_fade_in_rotate_up); + } else { + // Only keeping the default stream if it is not expended. + Iterator itr = mVolumeLineItems.iterator(); + while (itr.hasNext()) { + SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next(); + VolumeItem volumeItem = findVolumeItem(seekbarListItem); + if (!volumeItem.defaultItem) { + itr.remove(); + } else { + // Set progress here due to the progress of seekbar may not be updated. + seekbarListItem.setProgress(volumeItem.progress); + } + } + inAnimator = AnimatorInflater.loadAnimator( + mContext, R.anim.car_arrow_fade_in_rotate_down); + } + + Animator outAnimator = AnimatorInflater.loadAnimator( + mContext, R.anim.car_arrow_fade_out); + inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS); + AnimatorSet animators = new AnimatorSet(); + animators.playTogether(outAnimator, inAnimator); + animators.setTarget(v); + animators.start(); + mPagedListAdapter.notifyDataSetChanged(); + } + } + + private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener { + + private final int mVolumeGroupId; + private final CarAudioManager mCarAudioManager; + + private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) { + mVolumeGroupId = volumeGroupId; + mCarAudioManager = carAudioManager; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (!fromUser) { + // For instance, if this event is originated from AudioService, + // we can ignore it as it has already been handled and doesn't need to be + // sent back down again. + return; + } + try { + if (mCarAudioManager == null) { + Log.w(TAG, "Ignoring volume change event because the car isn't connected"); + return; + } + mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; + mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + } +} diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index 4b212c25c89e..0cad5af00267 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -19,8 +19,7 @@ package android.ext.services.notification; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.service.notification.Adjustment.KEY_IMPORTANCE; -import static android.service.notification.NotificationListenerService.Ranking - .USER_SENTIMENT_NEGATIVE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -241,7 +240,7 @@ public class Assistant extends NotificationAssistantService { signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies); } if (Settings.Secure.getInt(getContentResolver(), - Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) { + Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) { if (mNotificationCategorizer.shouldSilence(entry)) { final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance() : IMPORTANCE_LOW; diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index c9ee5c87de0f..1eb4b7494085 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -224,7 +224,7 @@ public class ExternalStorageProvider extends FileSystemProvider { root.flags |= Root.FLAG_REMOVABLE_USB; } - if (!VolumeInfo.ID_EMULATED_INTERNAL.equals(volume.getId())) { + if (volume.getType() != VolumeInfo.TYPE_EMULATED) { root.flags |= Root.FLAG_SUPPORTS_EJECT; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 56b768feee23..df5b1467f9d9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1165,6 +1165,12 @@ class SettingsProtoDumpUtil { GlobalSettingsProto.SmartSelection.UPDATE_METADATA_URL); p.end(smartSelectToken); + final long smartSuggestionsToken = p.start(GlobalSettingsProto.SMART_SUGGESTIONS); + dumpSetting(s, p, + Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED, + GlobalSettingsProto.SmartSuggestions.SERVICE_EXPLICITLY_ENABLED); + p.end(smartSuggestionsToken); + final long smsToken = p.start(GlobalSettingsProto.SMS); dumpSetting(s, p, Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS, @@ -1306,6 +1312,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.WARNING_TEMPERATURE, GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL); + dumpSetting(s, p, + Settings.Global.USB_ALARM_TEMPERATURE, + GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL); p.end(tempWarningToken); final long tetherToken = p.start(GlobalSettingsProto.TETHER); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 0b9b27f238d7..7d53c2f78151 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -223,6 +223,9 @@ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" /> + <!-- Permission to change the display color --> + <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml deleted file mode 100644 index f90cd69c1b39..000000000000 --- a/packages/SystemUI/res/drawable/car_ic_music.xml +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="56dp" - android:height="56dp" - android:viewportWidth="48" - android:viewportHeight="48"> - - <path - android:fillAlpha=".1" - android:strokeAlpha=".1" - android:pathData="M0 0h48v48H0z" /> - <path - android:fillColor="@color/car_grey_50" - android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14 -14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" /> -</vector> diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml deleted file mode 100644 index 61d937b90d04..000000000000 --- a/packages/SystemUI/res/drawable/car_ic_notification.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- - Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="56dp" - android:height="56dp" - android:viewportWidth="48" - android:viewportHeight="48"> - - <path - android:fillColor="#FFFFFF" - android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4 -4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73 -1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8 -9v12z" /> -</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml deleted file mode 100644 index 4651dcb3a229..000000000000 --- a/packages/SystemUI/res/drawable/car_ic_overview.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="56dp" - android:height="56dp" - android:viewportWidth="48" - android:viewportHeight="48"> - - <path - android:pathData="M0 0h48v48H0z" /> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82 -0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" /> -</vector> diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml deleted file mode 100644 index 25b449ab3a8a..000000000000 --- a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="?android:attr/colorBackgroundFloating" /> - <corners - android:bottomLeftRadius="@dimen/car_radius_3" - android:topLeftRadius="0dp" - android:bottomRightRadius="@dimen/car_radius_3" - android:topRightRadius="0dp" - /> -</shape> diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml deleted file mode 100644 index d568d0d3c179..000000000000 --- a/packages/SystemUI/res/layout/car_navigation_bar.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2016, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.systemui.statusbar.car.CarNavigationBarView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:background="@drawable/system_bar_background"> - - <LinearLayout - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:orientation="horizontal" - android:id="@+id/nav_buttons" - android:gravity="left" - android:paddingLeft="30dp" - android:layout_weight="1" - android:animateLayoutChanges="true"> - - <com.android.systemui.statusbar.car.CarNavigationButton - android:id="@+id/home" - android:layout_height="match_parent" - android:layout_width="wrap_content" - systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end" - android:src="@drawable/car_ic_overview" - android:background="?android:attr/selectableItemBackground" - android:paddingLeft="30dp" - android:paddingRight="30dp" - /> - - <com.android.systemui.statusbar.car.CarNavigationButton - android:id="@+id/hvac" - android:layout_height="match_parent" - android:layout_width="wrap_content" - systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" - systemui:broadcast="true" - android:src="@drawable/car_ic_hvac" - android:background="?android:attr/selectableItemBackground" - android:paddingLeft="30dp" - android:paddingRight="30dp" - /> - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="right" - android:orientation="horizontal"> - - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/notifications" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:src="@drawable/car_ic_notification" - android:background="?android:attr/selectableItemBackground" - android:paddingLeft="20dp" - android:paddingRight="20dp" - android:alpha="0.7" - /> - - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_clock_end_padding" - android:gravity="center_vertical" - android:paddingRight="20dp" - /> - - <Space - android:layout_width="10dp" - android:layout_height="match_parent"/> - - </LinearLayout> - -</com.android.systemui.statusbar.car.CarNavigationBarView> - diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml deleted file mode 100644 index 4ba6c06d80a1..000000000000 --- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2018, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.systemui.statusbar.car.CarNavigationBarView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:background="@drawable/system_bar_background"> - - <LinearLayout - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:orientation="horizontal" - android:id="@+id/nav_buttons" - android:gravity="left" - android:paddingLeft="30dp" - android:layout_weight="1" - android:animateLayoutChanges="true"> - - <com.android.systemui.statusbar.car.CarNavigationButton - android:id="@+id/home" - android:layout_height="match_parent" - android:layout_width="wrap_content" - systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end" - android:src="@drawable/car_ic_overview" - android:background="?android:attr/selectableItemBackground" - android:paddingLeft="30dp" - android:paddingRight="30dp" - /> - - <com.android.systemui.statusbar.car.CarNavigationButton - android:id="@+id/hvac" - android:layout_height="match_parent" - android:layout_width="wrap_content" - systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" - systemui:broadcast="true" - android:src="@drawable/car_ic_hvac" - android:background="?android:attr/selectableItemBackground" - android:paddingLeft="30dp" - android:paddingRight="30dp" - /> - </LinearLayout> -</com.android.systemui.statusbar.car.CarNavigationBarView> - diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml deleted file mode 100644 index f2ef30180bc0..000000000000 --- a/packages/SystemUI/res/layout/car_status_bar_header.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<!-- Extends LinearLayout --> -<com.android.systemui.qs.car.CarStatusBarHeader - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:id="@+id/header" - android:layout_width="match_parent" - android:layout_height="@dimen/car_qs_header_system_icons_area_height" - android:paddingStart="8dp" - android:paddingEnd="8dp" > - - <include layout="@layout/system_icons" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical|end" - android:layout_weight="1" - /> - - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_clock_end_padding" - android:gravity="center_vertical|end" - /> -</com.android.systemui.qs.car.CarStatusBarHeader> diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml deleted file mode 100644 index e16014bb8945..000000000000 --- a/packages/SystemUI/res/layout/car_top_navigation_bar.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** -** Copyright 2018, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.systemui.statusbar.car.CarNavigationBarView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:background="@drawable/system_bar_background"> - - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_clock_end_padding" - android:gravity="center_vertical" - /> - -</com.android.systemui.statusbar.car.CarNavigationBarView> - diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml deleted file mode 100644 index a6beaa15a4bf..000000000000 --- a/packages/SystemUI/res/layout/car_volume_dialog.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<androidx.car.widget.PagedListView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@drawable/car_card_rounded_background" - android:id="@+id/volume_list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/car_margin" - android:layout_marginEnd="@dimen/car_margin" - android:minWidth="@dimen/volume_dialog_panel_width" - android:theme="@style/Theme.Car.NoActionBar" - app:dividerStartMargin="@dimen/car_keyline_1" - app:dividerEndMargin="@dimen/car_keyline_1" - app:gutter="none" - app:showPagedListViewDivider="true" - app:scrollBarEnabled="false" /> diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 0cc3c9eb55dc..34c208ab81aa 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -56,11 +56,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - <ViewStub android:id="@+id/fullscreen_user_switcher_stub" - android:layout="@layout/car_fullscreen_user_switcher" - android:layout_width="match_parent" - android:layout_height="match_parent"/> - <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b0a519c00943..8e0bfb65428e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -881,6 +881,7 @@ <dimen name="smart_reply_button_stroke_width">1dp</dimen> <dimen name="smart_reply_button_font_size">14sp</dimen> <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. --> + <dimen name="smart_action_button_icon_size">24dp</dimen> <dimen name="smart_action_button_icon_padding">10dp</dimen> <!-- A reasonable upper bound for the height of the smart reply button. The measuring code diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml deleted file mode 100644 index 742dfdda73c8..000000000000 --- a/packages/SystemUI/res/xml/car_volume_items.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * - * Copyright 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ ---> - -<!-- - Defines all possible items on car volume settings UI, keyed by usage. - - This enables the CarSettings UI to associate VolumeGroups surfaced by - CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon) - for presentation. - - Order matters in this configuration. If one volume group contains multiple - audio usages, the first one appears in this file would be picked to be - presented on UI. - - When overriding this configuration, please consult also the - car_volume_groups.xml, which is read by car audio service. ---> -<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto"> - <item car:usage="unknown" - car:icon="@drawable/car_ic_music"/> - <item car:usage="media" - car:icon="@drawable/car_ic_music"/> - <item car:usage="voice_communication" - car:icon="@*android:drawable/ic_audio_ring_notif"/> - <item car:usage="voice_communication_signalling" - car:icon="@*android:drawable/ic_audio_ring_notif"/> - <item car:usage="alarm" - car:icon="@*android:drawable/ic_audio_alarm"/> - <item car:usage="notification" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="notification_ringtone" - car:icon="@*android:drawable/ic_audio_ring_notif"/> - <item car:usage="notification_communication_request" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="notification_communication_instant" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="notification_communication_delayed" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="notification_event" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="assistance_accessibility" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="assistance_navigation_guidance" - car:icon="@drawable/car_ic_navigation"/> - <item car:usage="assistance_sonification" - car:icon="@drawable/car_ic_notification"/> - <item car:usage="game" - car:icon="@drawable/car_ic_music"/> - <item car:usage="assistant" - car:icon="@drawable/car_ic_music"/> -</carVolumeItems> - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java index 12699d52772a..18dc185c6fbe 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java @@ -58,4 +58,8 @@ public class ActivityCompat { encoder.endStream(); return true; } + + public int getDisplayId() { + return mWrapped.getDisplayId(); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index b55aa5c8897d..904f94486e6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1904,13 +1904,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { + slotId + ", state=" + state +")"); } + boolean becameAbsent = false; if (!SubscriptionManager.isValidSubscriptionId(subId)) { Log.w(TAG, "invalid subId in handleSimStateChange()"); /* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */ if (state == State.ABSENT) { updateTelephonyCapable(true); + // Even though the subscription is not valid anymore, we need to notify that the + // SIM card was removed so we can update the UI. + becameAbsent = true; + } else { + return; } - return; } SimData data = mSimDatas.get(subId); @@ -1925,7 +1930,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { data.subId = subId; data.slotId = slotId; } - if (changed && state != State.UNKNOWN) { + if ((changed || becameAbsent) && state != State.UNKNOWN) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 053ea67b92c8..04c427f87125 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -88,6 +88,9 @@ public class BatteryMeterView extends LinearLayout implements private int mShowPercentMode = MODE_DEFAULT; private boolean mForceShowPercent; private boolean mShowPercentAvailable; + // Some places may need to show the battery conditionally, and not obey the tuner + private boolean mIgnoreTunerUpdates; + private boolean mIsSubscribedForTunerUpdates; private int mDarkModeBackgroundColor; private int mDarkModeFillColor; @@ -183,6 +186,44 @@ public class BatteryMeterView extends LinearLayout implements } /** + * Set {@code true} to turn off BatteryMeterView's subscribing to the tuner for updates, and + * thus avoid it controlling its own visibility + * + * @param ignore whether to ignore the tuner or not + */ + public void setIgnoreTunerUpdates(boolean ignore) { + mIgnoreTunerUpdates = ignore; + updateTunerSubscription(); + } + + private void updateTunerSubscription() { + if (mIgnoreTunerUpdates) { + unsubscribeFromTunerUpdates(); + } else { + subscribeForTunerUpdates(); + } + } + + private void subscribeForTunerUpdates() { + if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) { + return; + } + + Dependency.get(TunerService.class) + .addTunable(this, StatusBarIconController.ICON_BLACKLIST); + mIsSubscribedForTunerUpdates = true; + } + + private void unsubscribeFromTunerUpdates() { + if (!mIsSubscribedForTunerUpdates) { + return; + } + + Dependency.get(TunerService.class).removeTunable(this); + mIsSubscribedForTunerUpdates = false; + } + + /** * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll * revert back to dark-mode-based/tinted colors. * @@ -247,8 +288,7 @@ public class BatteryMeterView extends LinearLayout implements getContext().getContentResolver().registerContentObserver( Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser); updateShowPercent(); - Dependency.get(TunerService.class) - .addTunable(this, StatusBarIconController.ICON_BLACKLIST); + subscribeForTunerUpdates(); Dependency.get(ConfigurationController.class).addCallback(this); mUserTracker.startTracking(); } @@ -259,7 +299,7 @@ public class BatteryMeterView extends LinearLayout implements mUserTracker.stopTracking(); mBatteryController.removeCallback(this); getContext().getContentResolver().unregisterContentObserver(mSettingObserver); - Dependency.get(TunerService.class).removeTunable(this); + unsubscribeFromTunerUpdates(); Dependency.get(ConfigurationController.class).removeCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 417d5168641d..867c9175d308 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.SmartReplyConstants; +import com.android.systemui.volume.VolumeDialogComponent; import java.util.function.Consumer; @@ -132,6 +133,10 @@ public class SystemUIFactory { return new QSTileHost(context, statusBar, iconController); } + public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) { + return new VolumeDialogComponent(systemUi, context); + } + public void injectDependencies(ArrayMap<Object, DependencyProvider> providers, Context context) { providers.put(StatusBarStateController.class, StatusBarStateController::new); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java index a90a7d231dc1..ba89fe662a65 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java @@ -140,7 +140,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba createDialogs(); if (!mDialogs.isEmpty()) { - getComponent(CommandQueue.class).addCallbacks(this); + getComponent(CommandQueue.class).addCallback(this); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 416cc594051b..5c259d5c4093 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -342,13 +342,16 @@ public class BubbleController { } } } + boolean isCall = Notification.CATEGORY_CALL.equals(n.getNotification().category) + && n.isOngoing(); + boolean isMusic = n.getNotification().hasMediaSession(); + boolean isImportantOngoing = isMusic || isCall; Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle(); - boolean isMessageType = Notification.MessagingStyle.class.equals(style) - || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category) - || hasRemoteInput; - return (isMessageType && autoBubbleMessages) - || (n.isOngoing() && autoBubbleOngoing) + boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category); + boolean isMessageStyle = Notification.MessagingStyle.class.equals(style); + return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages) + || (isImportantOngoing && autoBubbleOngoing) || autoBubbleAll; } diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java deleted file mode 100644 index 09c000b469a7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.car; - -import android.content.Context; -import android.util.ArrayMap; - -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.ViewMediatorCallback; -import com.android.systemui.Dependency.DependencyProvider; -import com.android.systemui.SystemUIFactory; -import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.car.CarFacetButtonController; -import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; -import com.android.systemui.statusbar.car.hvac.HvacController; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; - -/** - * Class factory to provide car specific SystemUI components. - */ -public class CarSystemUIFactory extends SystemUIFactory { - - public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context, - ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) { - return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils); - } - - @Override - public void injectDependencies(ArrayMap<Object, DependencyProvider> providers, - Context context) { - super.injectDependencies(providers, context); - providers.put(NotificationEntryManager.class, - () -> new CarNotificationEntryManager(context)); - providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context)); - providers.put(HvacController.class, () -> new HvacController(context)); - providers.put(NotificationMediaManager.class, - () -> new CarNotificationMediaManager(context)); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index 2c61da343763..1718cff4e62b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -108,7 +108,7 @@ public class FalsingManager implements SensorEventListener { UserHandle.USER_ALL); updateConfiguration(); - Dependency.get(StatusBarStateController.class).addListener(mStateListener); + Dependency.get(StatusBarStateController.class).addCallback(mStateListener); } public static FalsingManager getInstance(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index aa085626b6c2..e8ef454bd466 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -45,7 +45,7 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa .withCallback(this::onExtensionCallback) .build(); mPlugin = mExtension.get(); - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this); + SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this); } private void onExtensionCallback(GlobalActions newPlugin) { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 039499858603..dc11b4c63d83 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -55,12 +55,12 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mContext = context; mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this); + SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this); } @Override public void destroy() { - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this); + SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this); if (mGlobalActions != null) { mGlobalActions.destroy(); mGlobalActions = null; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3b9110d31c6f..9ccdf79c37ca 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -488,6 +488,9 @@ public class KeyguardViewMediator extends SystemUI { // MVNO SIMs can become transiently NOT_READY when switching networks, // so we should only lock when they are ABSENT. onSimAbsentLocked(); + if (simWasLocked) { + resetStateLocked(); + } } } break; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 7792e177b601..37c8163702cf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -58,7 +58,7 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { : com.android.systemui.pip.phone.PipManager.getInstance(); mPipManager.initialize(mContext); - getComponent(CommandQueue.class).addCallbacks(this); + getComponent(CommandQueue.class).addCallback(this); putComponent(PipUI.class, this); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 953eb70cac5d..2acbea45a235 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -108,12 +108,12 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState); } } - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); + SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this); } @Override public void onDestroyView() { - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this); + SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this); super.onDestroyView(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index e2e943a369c2..d7d3981d05d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -79,7 +79,7 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon); mFooterIconId = R.drawable.ic_info_outline; mContext = context; - mMainHandler = new Handler(Looper.getMainLooper()); + mMainHandler = new Handler(Looper.myLooper()); mActivityStarter = Dependency.get(ActivityStarter.class); mSecurityController = Dependency.get(SecurityController.class); mHandler = new H(Dependency.get(Dependency.BG_LOOPER)); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index d1c2df53b5a5..ca8e824a223f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -308,7 +308,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { protected static List<String> loadTileSpecs(Context context, String tileList) { final Resources res = context.getResources(); final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); - if (tileList == null) { + if (TextUtils.isEmpty(tileList)) { tileList = res.getString(R.string.quick_settings_tiles); if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 427f638b0d30..3cecff033c91 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -232,6 +232,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements // Tint for the battery icons are handled in setupHost() mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon); mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF); + // Don't need to worry about tuner settings for this icon + mBatteryRemainingIcon.setIgnoreTunerUpdates(true); mBatteryRemainingText = findViewById(R.id.batteryRemainingText); mBatteryRemainingText.setTextColor(fillColor); diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 0702d74506fd..f13b565bd532 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -37,7 +37,7 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks { @Override public void start() { - getComponent(CommandQueue.class).addCallbacks(this); + getComponent(CommandQueue.class).addCallback(this); putComponent(Recents.class, this); mImpl = createRecentsImplementationFromConfig(); mImpl.onStart(mContext, this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 8b9399536969..95019ee2aeea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -35,6 +35,8 @@ import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.SystemUI; +import com.android.systemui.statusbar.CommandQueue.Callbacks; +import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; @@ -45,7 +47,7 @@ import java.util.ArrayList; * coalescing these calls so they don't stack up. For the calls * are coalesced, note that they are all idempotent. */ -public class CommandQueue extends IStatusBar.Stub { +public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> { private static final int INDEX_MASK = 0xffff; private static final int MSG_SHIFT = 16; private static final int MSG_MASK = 0xffff << MSG_SHIFT; @@ -183,12 +185,12 @@ public class CommandQueue extends IStatusBar.Stub { && !ONLY_CORE_APPS; } - public void addCallbacks(Callbacks callbacks) { + public void addCallback(Callbacks callbacks) { mCallbacks.add(callbacks); callbacks.disable(mDisable1, mDisable2, false /* animate */); } - public void removeCallbacks(Callbacks callbacks) { + public void removeCallback(Callbacks callbacks) { mCallbacks.remove(callbacks); } @@ -223,7 +225,9 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void disable(int state1, int state2) { + // TODO(b/117478341): Add multi-display support. + @Override + public void disable(int displayId, int state1, int state2) { disable(state1, state2, true); } @@ -266,8 +270,10 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, - int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + // TODO(b/117478341): Add multi-display support. + @Override + public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { synchronized (mLock) { // Don't coalesce these, since it might have one time flags set such as // STATUS_BAR_UNHIDE which might get lost. @@ -282,7 +288,9 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void topAppWindowChanged(boolean menuVisible) { + // TODO(b/117478341): Add multi-display support. + @Override + public void topAppWindowChanged(int displayId, boolean menuVisible) { synchronized (mLock) { mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED); mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0, @@ -290,7 +298,9 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + // TODO(b/117478341): Add multi-display support. + @Override + public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_IME_BUTTON); @@ -371,7 +381,9 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setWindowState(int window, int state) { + // TODO(b/117478341): Add multi-display support. + @Override + public void setWindowState(int displayId, int window, int state) { synchronized (mLock) { // don't coalesce these mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget(); @@ -385,7 +397,9 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void appTransitionPending() { + // TODO(b/117478341): Add multi-display support. + @Override + public void appTransitionPending(int displayId) { appTransitionPending(false /* forced */); } @@ -395,13 +409,17 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void appTransitionCancelled() { + // TODO(b/117478341): Add multi-display support. + @Override + public void appTransitionCancelled(int displayId) { synchronized (mLock) { mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED); } } - public void appTransitionStarting(long startTime, long duration) { + // TODO(b/117478341): Add multi-display support. + @Override + public void appTransitionStarting(int displayId, long startTime, long duration) { appTransitionStarting(startTime, duration, false /* forced */); } @@ -412,8 +430,9 @@ public class CommandQueue extends IStatusBar.Stub { } } + // TODO(b/117478341): Add multi-display support. @Override - public void appTransitionFinished() { + public void appTransitionFinished(int displayId) { synchronized (mLock) { mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 960d22185652..7d1b640f7e40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -156,7 +156,7 @@ public class KeyguardIndicationController implements StateListener { new IntentFilter(Intent.ACTION_TIME_TICK), null, Dependency.get(Dependency.TIME_TICK_HANDLER)); - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } /** @@ -167,7 +167,7 @@ public class KeyguardIndicationController implements StateListener { */ public void destroy() { mContext.unregisterReceiver(mTickReceiver); - Dependency.get(StatusBarStateController.class).removeListener(this); + Dependency.get(StatusBarStateController.class).removeCallback(this); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index b0724b1e014b..bba4369b5e01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -165,7 +165,7 @@ public class NotificationLockscreenUserManagerImpl implements mCurrentUserId = ActivityManager.getCurrentUser(); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); mLockPatternUtils = new LockPatternUtils(context); mKeyguardManager = context.getSystemService(KeyguardManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 7be5461f0afa..ecadf96dfbfc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -141,7 +141,7 @@ public class NotificationRemoteInputManager implements Dumpable { ActivityManager.getService().resumeAppSwitches(); } catch (RemoteException e) { } - return mCallback.handleRemoteViewClick(pendingIntent, () -> { + return mCallback.handleRemoteViewClick(view, pendingIntent, () -> { Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); options.second.setLaunchWindowingMode( WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); @@ -666,11 +666,13 @@ public class NotificationRemoteInputManager implements Dumpable { * Performs any special handling for a remote view click. The default behaviour can be * called through the defaultHandler parameter. * + * @param view * @param pendingIntent * @param defaultHandler * @return true iff the click was handled */ - boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler); + boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, + ClickHandler defaultHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index ac4abfc7fba5..6cec36a81e5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -121,13 +121,13 @@ public class NotificationShelf extends ActivatableNotificationView implements protected void onAttachedToWindow() { super.onAttachedToWindow(); Dependency.get(StatusBarStateController.class) - .addListener(mStateListener, StatusBarStateController.RANK_SHELF); + .addCallback(mStateListener, StatusBarStateController.RANK_SHELF); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - Dependency.get(StatusBarStateController.class).removeListener(mStateListener); + Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); } public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index daa2fd45b142..dc3a60786ce2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -129,7 +129,7 @@ public class NotificationViewHierarchyManager { res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications); mStatusBarStateListener = new StatusBarStateListener(mBubbleController); mEntryManager.setStatusBarStateListener(mStatusBarStateListener); - Dependency.get(StatusBarStateController.class).addListener(mStatusBarStateListener); + Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener); } public void setUpWithPresenter(NotificationPresenter presenter, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java index eaf52cb357fe..3f84416ad575 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java @@ -26,8 +26,10 @@ import android.view.animation.Interpolator; import com.android.internal.annotations.GuardedBy; import com.android.systemui.Interpolators; +import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.CallbackController; import java.lang.annotation.Retention; import java.util.ArrayList; @@ -36,7 +38,7 @@ import java.util.Comparator; /** * Tracks and reports on {@link StatusBarState}. */ -public class StatusBarStateController { +public class StatusBarStateController implements CallbackController<StateListener> { private static final String TAG = "SbStateController"; private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER; @@ -228,7 +230,7 @@ public class StatusBarStateController { return mLastState == StatusBarState.SHADE_LOCKED; } - public void addListener(StateListener listener) { + public void addCallback(StateListener listener) { synchronized (mListeners) { addListenerInternalLocked(listener, Integer.MAX_VALUE); } @@ -244,7 +246,7 @@ public class StatusBarStateController { * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking * (i.e., they are non-dependent on the order of operations of StatusBarState listeners). */ - public void addListener(StateListener listener, @SbStateListenerRank int rank) { + public void addCallback(StateListener listener, @SbStateListenerRank int rank) { synchronized (mListeners) { addListenerInternalLocked(listener, rank); } @@ -264,7 +266,7 @@ public class StatusBarStateController { mListeners.sort(mComparator); } - public void removeListener(StateListener listener) { + public void removeCallback(StateListener listener) { synchronized (mListeners) { mListeners.removeIf((it) -> it.listener.equals(listener)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java deleted file mode 100644 index bd328567fb46..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.android.systemui.statusbar.car; - -import android.content.Context; -import android.content.Intent; -import android.content.res.TypedArray; -import android.os.UserHandle; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.android.keyguard.AlphaOptimizedImageButton; -import com.android.systemui.Dependency; -import com.android.systemui.R; - -/** - * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined - * category. It can also render a indicator impling that there are more options of apps to launch - * using this component. This is done with a "More icon" currently an arrow as defined in the layout - * file. The class is to serve as an example. - * Usage example: A button that allows a user to select a music app and indicate that there are - * other music apps installed. - */ -public class CarFacetButton extends LinearLayout { - private static final String FACET_FILTER_DELIMITER = ";"; - /** - * Extra information to be sent to a helper to make the decision of what app to launch when - * clicked. - */ - private static final String EXTRA_FACET_CATEGORIES = "categories"; - private static final String EXTRA_FACET_PACKAGES = "packages"; - private static final String EXTRA_FACET_ID = "filter_id"; - private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker"; - - private Context mContext; - private AlphaOptimizedImageButton mIcon; - private AlphaOptimizedImageButton mMoreIcon; - private boolean mSelected = false; - private String[] mComponentNames; - /** App categories that are to be used with this widget */ - private String[] mFacetCategories; - /** App packages that are allowed to be used with this widget */ - private String[] mFacetPackages; - private int mIconResourceId; - /** - * If defined in the xml this will be the icon that's rendered when the button is marked as - * selected - */ - private int mSelectedIconResourceId; - private boolean mUseMoreIcon = true; - private float mSelectedAlpha = 1f; - private float mUnselectedAlpha = 1f; - - - public CarFacetButton(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - View.inflate(context, R.layout.car_facet_button, this); - - // extract custom attributes - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton); - setupIntents(typedArray); - setupIcons(typedArray); - CarFacetButtonController carFacetButtonController = Dependency.get( - CarFacetButtonController.class); - carFacetButtonController.addFacetButton(this); - - } - - /** - * Reads the custom attributes to setup click handlers for this component. - */ - private void setupIntents(TypedArray typedArray) { - String intentString = typedArray.getString(R.styleable.CarFacetButton_intent); - String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent); - String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories); - String packageString = typedArray.getString(R.styleable.CarFacetButton_packages); - String componentNameString = - typedArray.getString(R.styleable.CarFacetButton_componentNames); - try { - final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); - intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId())); - - if (packageString != null) { - mFacetPackages = packageString.split(FACET_FILTER_DELIMITER); - intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages); - } - if (categoryString != null) { - mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER); - intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories); - } - if (componentNameString != null) { - mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER); - } - - setOnClickListener(v -> { - intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - }); - - if (longPressIntentString != null) { - final Intent longPressIntent = Intent.parseUri(longPressIntentString, - Intent.URI_INTENT_SCHEME); - setOnLongClickListener(v -> { - mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT); - return true; - }); - } - } catch (Exception e) { - throw new RuntimeException("Failed to attach intent", e); - } - } - - - private void setupIcons(TypedArray styledAttributes) { - mSelectedAlpha = styledAttributes.getFloat( - R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha); - mUnselectedAlpha = styledAttributes.getFloat( - R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha); - mIcon = findViewById(R.id.car_nav_button_icon); - mIcon.setScaleType(ImageView.ScaleType.CENTER); - mIcon.setClickable(false); - mIcon.setAlpha(mUnselectedAlpha); - mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); - mIcon.setImageResource(mIconResourceId); - mSelectedIconResourceId = styledAttributes.getResourceId( - R.styleable.CarFacetButton_selectedIcon, mIconResourceId); - - mMoreIcon = findViewById(R.id.car_nav_button_more_icon); - mMoreIcon.setClickable(false); - mMoreIcon.setAlpha(mSelectedAlpha); - mMoreIcon.setVisibility(GONE); - mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true); - } - - /** - * @return The app categories the component represents - */ - public String[] getCategories() { - if (mFacetCategories == null) { - return new String[0]; - } - return mFacetCategories; - } - - /** - * @return The valid packages that should be considered. - */ - public String[] getFacetPackages() { - if (mFacetPackages == null) { - return new String[0]; - } - return mFacetPackages; - } - - public String[] getComponentName() { - if (mComponentNames == null) { - return new String[0]; - } - return mComponentNames; - } - - /** - * Updates the alpha of the icons to "selected" and shows the "More icon" - * @param selected true if the view must be selected, false otherwise - */ - public void setSelected(boolean selected) { - super.setSelected(selected); - setSelected(selected, selected); - } - - /** - * Updates the visual state to let the user know if it's been selected. - * @param selected true if should update the alpha of the icon to selected, false otherwise - * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this - * is ignored if the attribute useMoreIcon is set to false - */ - public void setSelected(boolean selected, boolean showMoreIcon) { - mSelected = selected; - mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha); - mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId); - if (mUseMoreIcon) { - mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index f506753379a4..f899863dcc6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -299,6 +299,10 @@ public class ActivityLaunchAnimator { return top; } + public int getBottom() { + return bottom; + } + public int getWidth() { return right - left; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java index 1f48c15e63c0..09eb8a1030ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java @@ -75,6 +75,6 @@ public class NotificationUtils { /** Returns the value of the new interruption model setting. */ public static boolean useNewInterruptionModel(Context context) { return Settings.Secure.getInt(context.getContentResolver(), - NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0; + NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 87313b8a0393..9f02e543b6e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -149,7 +149,7 @@ public class NotificationLogger implements StateListener { mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); // Not expected to be destroyed, don't need to unsubscribe - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } public void setUpWithContainer(NotificationListContainer listContainer) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 63fe04aff08b..8214ea6c7616 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -328,6 +328,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private float mTranslationWhenRemoved; private boolean mWasChildInGroupWhenRemoved; private int mNotificationColorAmbient; + private NotificationInlineImageResolver mImageResolver; private SystemNotificationAsyncTask mSystemNotificationAsyncTask = new SystemNotificationAsyncTask(); @@ -1621,6 +1622,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mFalsingManager = FalsingManager.getInstance(context); mNotificationInflater = new NotificationInflater(this); mMenuRow = new NotificationMenuRow(mContext); + mImageResolver = new NotificationInlineImageResolver(context, + new NotificationInlineImageCache()); initDimens(); } @@ -1657,6 +1660,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded); } + NotificationInlineImageResolver getImageResolver() { + return mImageResolver; + } + /** * Resets this view so it can be re-used for an updated notification. */ @@ -2008,7 +2015,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress()); int startClipTopAmount = params.getStartClipTopAmount(); if (mNotificationParent != null) { - top -= mNotificationParent.getTranslationY(); + float parentY = mNotificationParent.getTranslationY(); + top -= parentY; mNotificationParent.setTranslationZ(translationZ); int parentStartClipTopAmount = params.getParentStartClipTopAmount(); if (startClipTopAmount != 0) { @@ -2018,8 +2026,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationParent.setClipTopAmount(clipTopAmount); } mNotificationParent.setExtraWidthForClipping(extraWidthForClipping); - mNotificationParent.setMinimumHeightForClipping(params.getHeight() - + mNotificationParent.getActualHeight()); + float clipBottom = Math.max(params.getBottom(), + parentY + mNotificationParent.getActualHeight() + - mNotificationParent.getClipBottomAmount()); + float clipTop = Math.min(params.getTop(), parentY); + int minimumHeightForClipping = (int) (clipBottom - clipTop); + mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping); } else if (startClipTopAmount != 0) { int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation); setClipTopAmount(clipTopAmount); @@ -2085,7 +2097,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected boolean shouldClipToActualHeight() { - return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding; + return super.shouldClipToActualHeight() && !mExpandAnimationRunning; } @Override @@ -3129,6 +3141,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", alpha: " + getAlpha()); pw.print(", translation: " + getTranslation()); pw.print(", removed: " + isRemoved()); + pw.print(", expandAnimationRunning: " + mExpandAnimationRunning); NotificationContentView showingLayout = getShowingLayout(); pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); pw.println(); @@ -3142,6 +3155,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.println(); pw.println(); if (mIsSummaryWithChildren) { + pw.print(" ChildrenContainer"); + pw.print(" visibility: " + mChildrenContainer.getVisibility()); + pw.print(", alpha: " + mChildrenContainer.getAlpha()); + pw.print(", translationY: " + mChildrenContainer.getTranslationY()); + pw.println(); List<ExpandableNotificationRow> notificationChildren = getNotificationChildren(); pw.println(" Children: " + notificationChildren.size()); pw.println(" {"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 0efb1308e83e..6aadcb7b30e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -77,8 +77,6 @@ public abstract class ExpandableOutlineView extends ExpandableView { protected boolean mShouldTranslateContents; private boolean mTopAmountRounded; private float mDistanceToTopRoundness = -1; - private float mExtraWidthForClipping; - private int mMinimumHeightForClipping = 0; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @Override @@ -219,13 +217,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { return result; } + @Override public void setExtraWidthForClipping(float extraWidthForClipping) { - mExtraWidthForClipping = extraWidthForClipping; + super.setExtraWidthForClipping(extraWidthForClipping); invalidate(); } + @Override public void setMinimumHeightForClipping(int minimumHeightForClipping) { - mMinimumHeightForClipping = minimumHeightForClipping; + super.setMinimumHeightForClipping(minimumHeightForClipping); invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 1e8de076cbed..20c48163e6b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -48,6 +48,8 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { private int mActualHeight; protected int mClipTopAmount; protected int mClipBottomAmount; + protected int mMinimumHeightForClipping = 0; + protected float mExtraWidthForClipping = 0; private boolean mDark; private ArrayList<View> mMatchParentViews = new ArrayList<View>(); private static Rect mClipRect = new Rect(); @@ -398,14 +400,26 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { protected void updateClipping() { if (mClipToActualHeight && shouldClipToActualHeight()) { int top = getClipTopAmount(); - mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding() - - mClipBottomAmount, top)); + int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding() + - mClipBottomAmount, top), mMinimumHeightForClipping); + int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f); + mClipRect.set(-halfExtraWidth, top, getWidth() + halfExtraWidth, bottom); setClipBounds(mClipRect); } else { setClipBounds(null); } } + public void setMinimumHeightForClipping(int minimumHeightForClipping) { + mMinimumHeightForClipping = minimumHeightForClipping; + updateClipping(); + } + + public void setExtraWidthForClipping(float extraWidthForClipping) { + mExtraWidthForClipping = extraWidthForClipping; + updateClipping(); + } + public float getHeaderVisibleAmount() { return 1.0f; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java index 7086025836cb..9908049984d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java @@ -33,6 +33,7 @@ import android.view.View; import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.ImageMessageConsumer; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.MediaNotificationProcessor; @@ -114,7 +115,7 @@ public class NotificationInflater { @InflationFlag private int mInflationFlags = REQUIRED_INFLATION_FLAGS; - private static final InflationExecutor EXECUTOR = new InflationExecutor(); + static final InflationExecutor EXECUTOR = new InflationExecutor(); private final ExpandableNotificationRow mRow; private boolean mIsLowPriority; @@ -244,6 +245,10 @@ public class NotificationInflater { // Only inflate the ones that are set. reInflateFlags &= mInflationFlags; StatusBarNotification sbn = mRow.getEntry().notification; + + // To check if the notification has inline image and preload inline image if necessary. + mRow.getImageResolver().preloadImages(sbn.getNotification()); + AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews, mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler); @@ -520,8 +525,14 @@ public class NotificationInflater { } return; } - RemoteViews.OnViewAppliedListener listener - = new RemoteViews.OnViewAppliedListener() { + RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() { + + @Override + public void onViewInflated(View v) { + if (v instanceof ImageMessageConsumer) { + ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver()); + } + } @Override public void onViewApplied(View v) { @@ -697,7 +708,7 @@ public class NotificationInflater { && newView.getPackage() != null && newView.getPackage().equals(oldView.getPackage()) && newView.getLayoutId() == oldView.getLayoutId() - && !oldView.isReapplyDisallowed()); + && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED)); } public void setInflationCallback(InflationCallback callback) { @@ -851,6 +862,10 @@ public class NotificationInflater { mRow.getEntry().onInflationTaskFinished(); mRow.onNotificationUpdated(); mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags); + + // Notify the resolver that the inflation task has finished, + // try to purge unnecessary cached entries. + mRow.getImageResolver().purgeCache(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java new file mode 100644 index 000000000000..8c8bad2ab196 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row; + +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; + +/** + * A cache for inline images of image messages. + */ +public class NotificationInlineImageCache implements NotificationInlineImageResolver.ImageCache { + private static final String TAG = NotificationInlineImageCache.class.getSimpleName(); + + private NotificationInlineImageResolver mResolver; + private final ConcurrentHashMap<Uri, PreloadImageTask> mCache; + + public NotificationInlineImageCache() { + mCache = new ConcurrentHashMap<>(); + } + + @Override + public void setImageResolver(NotificationInlineImageResolver resolver) { + mResolver = resolver; + } + + @Override + public boolean hasEntry(Uri uri) { + return mCache.containsKey(uri); + } + + @Override + public void preload(Uri uri) { + PreloadImageTask newTask = new PreloadImageTask(mResolver); + newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri); + mCache.put(uri, newTask); + } + + @Override + public Drawable get(Uri uri) { + Drawable result = null; + try { + result = mCache.get(uri).get(); + } catch (InterruptedException | ExecutionException ex) { + Log.d(TAG, "get: Failed get image from " + uri); + } + return result; + } + + @Override + public void purge() { + Set<Uri> wantedSet = mResolver.getWantedUriSet(); + mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); + } + + private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> { + private final NotificationInlineImageResolver mResolver; + + PreloadImageTask(NotificationInlineImageResolver resolver) { + mResolver = resolver; + } + + @Override + protected Drawable doInBackground(Uri... uris) { + Drawable drawable = null; + Uri target = uris[0]; + + try { + drawable = mResolver.resolveImage(target); + } catch (IOException ex) { + Log.d(TAG, "PreloadImageTask: Resolve failed from " + target); + } + + return drawable; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java new file mode 100644 index 000000000000..588246f3d2c6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row; + +import android.app.ActivityManager; +import android.app.Notification; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; + +import com.android.internal.widget.ImageResolver; +import com.android.internal.widget.LocalImageResolver; +import com.android.internal.widget.MessagingMessage; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Custom resolver with built-in image cache for image messages. + */ +public class NotificationInlineImageResolver implements ImageResolver { + private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + + private final Context mContext; + private final ImageCache mImageCache; + private Set<Uri> mWantedUriSet; + + /** + * Constructor. + * @param context Context. + * @param imageCache The implementation of internal cache. + */ + public NotificationInlineImageResolver(Context context, ImageCache imageCache) { + mContext = context.getApplicationContext(); + mImageCache = imageCache; + + if (mImageCache != null) { + mImageCache.setImageResolver(this); + } + } + + /** + * Check if this resolver has its internal cache implementation. + * @return True if has its internal cache, false otherwise. + */ + public boolean hasCache() { + return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); + } + + /** + * To resolve image from specified uri directly. + * @param uri Uri of the image. + * @return Drawable of the image. + * @throws IOException Throws if failed at resolving the image. + */ + Drawable resolveImage(Uri uri) throws IOException { + return LocalImageResolver.resolveImage(uri, mContext); + } + + @Override + public Drawable loadImage(Uri uri) { + Drawable result = null; + try { + result = hasCache() ? mImageCache.get(uri) : resolveImage(uri); + } catch (IOException ex) { + Log.d(TAG, "loadImage: Can't load image from " + uri); + } + return result; + } + + /** + * Resolve the message list from specified notification and + * refresh internal cache according to the result. + * @param notification The Notification to be resolved. + */ + public void preloadImages(Notification notification) { + if (!hasCache()) { + return; + } + + retrieveWantedUriSet(notification); + Set<Uri> wantedSet = getWantedUriSet(); + wantedSet.forEach(uri -> { + if (!mImageCache.hasEntry(uri)) { + // The uri is not in the cache, we need trigger a loading task for it. + mImageCache.preload(uri); + } + }); + } + + /** + * Try to purge unnecessary cache entries. + */ + public void purgeCache() { + if (!hasCache()) { + return; + } + mImageCache.purge(); + } + + private void retrieveWantedUriSet(Notification notification) { + Parcelable[] messages; + Parcelable[] historicMessages; + List<Notification.MessagingStyle.Message> messageList; + List<Notification.MessagingStyle.Message> historicList; + Set<Uri> result = new HashSet<>(); + + Bundle extras = notification.extras; + if (extras == null) { + return; + } + + messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); + messageList = messages == null ? null : + Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); + if (messageList != null) { + for (Notification.MessagingStyle.Message message : messageList) { + if (MessagingMessage.hasImage(message)) { + result.add(message.getDataUri()); + } + } + } + + historicMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES); + historicList = historicMessages == null ? null : + Notification.MessagingStyle.Message.getMessagesFromBundleArray(historicMessages); + if (historicList != null) { + for (Notification.MessagingStyle.Message historic : historicList) { + if (MessagingMessage.hasImage(historic)) { + result.add(historic.getDataUri()); + } + } + } + + mWantedUriSet = result; + } + + Set<Uri> getWantedUriSet() { + return mWantedUriSet; + } + + /** + * A interface for internal cache implementation of this resolver. + */ + interface ImageCache { + /** + * Load the image from cache first then resolve from uri if missed the cache. + * @param uri The uri of the image. + * @return Drawable of the image. + */ + Drawable get(Uri uri); + + /** + * Set the image resolver that actually resolves image from specified uri. + * @param resolver The resolver implementation that resolves image from specified uri. + */ + void setImageResolver(NotificationInlineImageResolver resolver); + + /** + * Check if the uri is in the cache no matter it is loading or loaded. + * @param uri The uri to check. + * @return True if it is already in the cache; false otherwise. + */ + boolean hasEntry(Uri uri); + + /** + * Start a new loading task for the target uri. + * @param uri The target to load. + */ + void preload(Uri uri); + + /** + * Purge unnecessary entries in the cache. + */ + void purge(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 5d640e0216e0..eca1a1411212 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -618,7 +618,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected void onAttachedToWindow() { super.onAttachedToWindow(); Dependency.get(StatusBarStateController.class) - .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER); + .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER); Dependency.get(ConfigurationController.class).addCallback(this); } @@ -626,7 +626,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - Dependency.get(StatusBarStateController.class).removeListener(mStateListener); + Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); Dependency.get(ConfigurationController.class).removeCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 3b13fe96fe42..24570aec9db4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -120,15 +120,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onResume() { super.onResume(); - mCommandQueue.addCallbacks(this); - mStatusBarStateController.addListener(this); + mCommandQueue.addCallback(this); + mStatusBarStateController.addCallback(this); } @Override public void onPause() { super.onPause(); - mCommandQueue.removeCallbacks(this); - mStatusBarStateController.removeListener(this); + mCommandQueue.removeCallback(this); + mStatusBarStateController.removeCallback(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 94b2cdeea898..cfa751cec613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -88,7 +88,7 @@ public class DozeScrimController implements StateListener { public DozeScrimController(DozeParameters dozeParameters) { mDozeParameters = dozeParameters; //Never expected to be destroyed - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 9faada05294b..aa0b7b656e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -129,7 +129,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, updateTouchableRegionListener(); } }); - Dependency.get(StatusBarStateController.class).addListener(mStateListener); + Dependency.get(StatusBarStateController.class).addCallback(mStateListener); mBubbleController.setBubbleStateChangeListener((hasBubbles) -> { if (!hasBubbles) { mBubbleGoingAway = true; @@ -143,7 +143,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } public void destroy() { - Dependency.get(StatusBarStateController.class).removeListener(mStateListener); + Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); } private void initResources() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index b29889dac6f8..57cc7d6c1ecb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -73,15 +73,15 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mStatusBarStateController = Dependency.get(StatusBarStateController.class); SysUiServiceProvider.getComponent(context, CommandQueue.class) - .addCallbacks(this); - mStatusBarStateController.addListener(this); + .addCallback(this); + mStatusBarStateController.addCallback(this); mDozeAmount = mStatusBarStateController.getDozeAmount(); } public void destroy(Context context) { SysUiServiceProvider.getComponent(context, CommandQueue.class) - .removeCallbacks(this); - mStatusBarStateController.removeListener(this); + .removeCallback(this); + mStatusBarStateController.removeCallback(this); } public void saveState(Bundle outState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 8657003891be..d2bfd123f113 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -199,7 +199,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); - mCommandQueue.addCallbacks(this); + mCommandQueue.addCallback(this); mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class); mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); @@ -225,7 +225,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { @Override public void onDestroy() { super.onDestroy(); - mCommandQueue.removeCallbacks(this); + mCommandQueue.removeCallback(this); Dependency.get(AccessibilityManagerWrapper.class).removeCallback( mAccessibilityListener); mContentResolver.unregisterContentObserver(mMagnificationObserver); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index 12a0cc882a81..3984405f8e09 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -24,7 +24,6 @@ import android.util.SparseArray; import android.view.Display; import android.view.IWallpaperVisibilityListener; import android.view.IWindowManager; -import android.view.MotionEvent; import android.view.View; import com.android.internal.statusbar.IStatusBarService; @@ -165,23 +164,4 @@ public final class NavigationBarTransitions extends BarTransitions { } mView.onDarkIntensityChange(darkIntensity); } - - private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - // even though setting the systemUI visibility below will turn these views - // on, we need them to come up faster so that they can catch this motion - // event - applyLightsOut(false, false, false); - - try { - mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE, - "LightsOutListener"); - } catch (android.os.RemoteException ex) { - } - } - return false; - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index 3e31fa06ef27..2a68fa598603 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -82,7 +82,7 @@ public class NotificationGroupAlertTransferHelper implements OnGroupChangeListen private boolean mIsDozing; public NotificationGroupAlertTransferHelper() { - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 448b5c38da51..8f4369a98b17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -55,7 +55,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, private boolean mIsUpdatingUnchangedGroup; public NotificationGroupManager() { - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } /** 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 33d176a580da..a2a11bbfd650 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -366,7 +366,7 @@ public class NotificationPanelView extends PanelView implements protected void onAttachedToWindow() { super.onAttachedToWindow(); FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener); - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); Dependency.get(ZenModeController.class).addCallback(this); Dependency.get(ConfigurationController.class).addCallback(this); } @@ -375,7 +375,7 @@ public class NotificationPanelView extends PanelView implements protected void onDetachedFromWindow() { super.onDetachedFromWindow(); FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener); - Dependency.get(StatusBarStateController.class).removeListener(this); + Dependency.get(StatusBarStateController.class).removeCallback(this); Dependency.get(ZenModeController.class).removeCallback(this); Dependency.get(ConfigurationController.class).removeCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index c84f3db8acb0..ee1eb42a07f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -268,7 +268,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mLocationController.addCallback(this); mPrivacyItemController.setListening(true); - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this); + SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener); // Clear out all old notifications on startup (only present in the case where sysui dies) @@ -296,7 +296,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mKeyguardMonitor.removeCallback(this); mLocationController.removeCallback(this); mPrivacyItemController.setListening(false); - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this); + SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this); mContext.unregisterReceiver(mIntentReceiver); NotificationManager noMan = mContext.getSystemService(NotificationManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index 2cbf27c6e61c..4983618ba414 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -185,6 +185,7 @@ public class QuickStepController implements GestureHelper { // Requires proxy and an active gesture or able to perform any gesture to continue if (mOverviewEventSender.getProxy() == null + || !mOverviewEventSender.shouldShowSwipeUpUI() || (mCurrentAction == null && !canPerformAnyAction())) { return deadZoneConsumed; } 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 5723948bc293..96ec6be1f957 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -161,7 +161,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTileHost; -import com.android.systemui.qs.car.CarQSFragment; import com.android.systemui.recents.Recents; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -580,6 +579,7 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mVibrateOnOpening; private VibratorHelper mVibratorHelper; protected NotificationPresenter mPresenter; + private boolean mPulsing; @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { @@ -628,7 +628,7 @@ public class StatusBar extends SystemUI implements DemoMode, mBubbleController.setExpandListener(mBubbleExpandListener); mColorExtractor.addOnColorsChangedListener(this); - mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR); + mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mDreamManager = IDreamManager.Stub.asInterface( @@ -664,7 +664,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Connect in to the status bar manager service mCommandQueue = getComponent(CommandQueue.class); - mCommandQueue.addCallbacks(this); + mCommandQueue.addCallback(this); int[] switches = new int[9]; ArrayList<IBinder> binders = new ArrayList<>(); @@ -920,8 +920,7 @@ public class StatusBar extends SystemUI implements DemoMode, Dependency.get(ExtensionController.class) .newExtension(QS.class) .withPlugin(QS.class) - .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new) - .withDefault(QSFragment::new) + .withDefault(this::createDefaultQSFragment) .build()); final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mIconController); @@ -1009,6 +1008,10 @@ public class StatusBar extends SystemUI implements DemoMode, ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); } + protected QS createDefaultQSFragment() { + return new QSFragment(); + } + protected void setUpPresenter() { // Set up the initial notification state. mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, @@ -1542,7 +1545,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isPulsing() { - return mAmbientPulseManager.hasNotifications(); + return mPulsing; } public boolean isLaunchTransitionFadingAway() { @@ -2524,7 +2527,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } if (dismissShade) { - if (mExpandedVisible) { + if (mExpandedVisible && !mBouncerShowing) { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, true /* delayed*/); } else { @@ -2887,7 +2890,7 @@ public class StatusBar extends SystemUI implements DemoMode, mContext.unregisterReceiver(mDemoReceiver); mAssistManager.destroy(); mHeadsUpManager.destroy(); - mStatusBarStateController.removeListener(this); + mStatusBarStateController.removeCallback(this); if (mQSPanel != null && mQSPanel.getHost() != null) { mQSPanel.getHost().destroy(); @@ -3205,6 +3208,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.onAffordanceLaunchEnded(); mNotificationPanel.animate().cancel(); mNotificationPanel.setAlpha(1f); + updateScrimController(); Trace.endSection(); return staying; } @@ -3933,6 +3937,10 @@ public class StatusBar extends SystemUI implements DemoMode, return; } + // Set the state to pulsing, so ScrimController will know what to do once we ask it to + // execute the transition. The pulse callback will then be invoked when the scrims + // are black, indicating that StatusBar is ready to present the rest of the UI. + mPulsing = true; mDozeScrimController.pulse(new PulseCallback() { @Override public void onPulseStarted() { @@ -3946,6 +3954,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPulseFinished() { + mPulsing = false; callback.onPulseFinished(); setPulsing(false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 7c17c018443e..4f25349ef7d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -76,7 +76,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu loadDimens(); SysUiServiceProvider.getComponent(context, CommandQueue.class) - .addCallbacks(this); + .addCallback(this); Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST); } 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 484fe110b28a..0f8970f1069f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -151,7 +151,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLockPatternUtils = lockPatternUtils; mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback); - Dependency.get(StatusBarStateController.class).addListener(this); + Dependency.get(StatusBarStateController.class).addCallback(this); } public void registerStatusBar(StatusBar statusBar, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index a743d41e8d3a..e3f6bd8f21f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -70,10 +70,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks { mContext = context; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); - mStatusBarStateController.addListener(mStateListener); + mStatusBarStateController.addCallback(mStateListener); mKeyguardManager = context.getSystemService(KeyguardManager.class); mCommandQueue = getComponent(context, CommandQueue.class); - mCommandQueue.addCallbacks(this); + mCommandQueue.addCallback(this); } private void setStatusBarState(int state) { @@ -206,7 +206,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks { } @Override - public boolean handleRemoteViewClick(PendingIntent pendingIntent, + public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) { final boolean isActivity = pendingIntent.isActivity(); if (isActivity) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 158ee8ae8fbd..986a86d13958 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -92,7 +92,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); mDozeParameters = dozeParameters; mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); - Dependency.get(StatusBarStateController.class).addListener( + Dependency.get(StatusBarStateController.class).addCallback( mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); Dependency.get(ConfigurationController.class).addCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index c2af95e66fe9..aafdcd50b8e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -187,7 +187,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C null, Dependency.get(Dependency.TIME_TICK_HANDLER)); Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS, StatusBarIconController.ICON_BLACKLIST); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); + SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this); if (mShowDark) { Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); } @@ -215,7 +215,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C mAttached = false; Dependency.get(TunerService.class).removeTunable(this); SysUiServiceProvider.getComponent(getContext(), CommandQueue.class) - .removeCallbacks(this); + .removeCallback(this); if (mShowDark) { Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index f36066ce3794..2a4336e809a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -21,7 +21,6 @@ import android.text.TextPaint; import android.text.method.TransformationMethod; import android.util.AttributeSet; import android.util.Log; -import android.util.Size; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,6 +40,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.text.BreakIterator; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; @@ -282,9 +282,9 @@ public class SmartReplyView extends ViewGroup { Drawable iconDrawable = action.getIcon().loadDrawable(context); // Add the action icon to the Smart Action button. - Size newIconSize = calculateIconSizeFromSingleLineButton(context, root, - new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight())); - iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight()); + int newIconSize = context.getResources().getDimensionPixelSize( + R.dimen.smart_action_button_icon_size); + iconDrawable.setBounds(0, 0, newIconSize, newIconSize); button.setCompoundDrawables(iconDrawable, null, null, null); button.setOnClickListener(view -> @@ -295,30 +295,12 @@ public class SmartReplyView extends ViewGroup { // TODO(b/119010281): handle accessibility + // Mark this as an Action button + final LayoutParams lp = (LayoutParams) button.getLayoutParams(); + lp.buttonType = SmartButtonType.ACTION; return button; } - private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root, - Size originalIconSize) { - Button button = (Button) LayoutInflater.from(context).inflate( - R.layout.smart_action_button, root, false); - // Add simple text here to ensure the button displays one line of text. - button.setText("a"); - return calculateIconSizeFromButtonHeight(button, originalIconSize); - } - - // Given a button with text on a single line - we want to add an icon to that button. This - // method calculates the icon height to use to avoid making the button grow in height. - private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) { - // A completely permissive measure spec should make the button text single-line. - button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH); - int buttonHeight = button.getMeasuredHeight(); - int newIconHeight = buttonHeight / 2; - int newIconWidth = (int) (originalIconSize.getWidth() - * ((double) newIconHeight) / originalIconSize.getHeight()); - return new Size(newIconWidth, newIconHeight); - } - @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(mContext, attrs); @@ -352,18 +334,26 @@ public class SmartReplyView extends ViewGroup { int displayedChildCount = 0; int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal; - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); + // Set up a list of suggestions where actions come before replies. Note that the Buttons + // themselves have already been added to the view hierarchy in an order such that Smart + // Replies are shown before Smart Actions. The order of the list below determines which + // suggestions will be shown at all - only the first X elements are shown (where X depends + // on how much space each suggestion button needs). + List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION); + List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY); + List<View> smartSuggestions = new ArrayList<>(smartActions); + smartSuggestions.addAll(smartReplies); + List<View> coveredSuggestions = new ArrayList<>(); + + for (View child : smartSuggestions) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) { - continue; - } child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(), buttonPaddingHorizontal, child.getPaddingBottom()); child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec); + coveredSuggestions.add(child); + final int lineCount = ((Button) child).getLineCount(); if (lineCount < 1 || lineCount > 2) { // If smart reply has no text, or more than two lines, then don't show it. @@ -417,7 +407,8 @@ public class SmartReplyView extends ViewGroup { // Mark all buttons from the last squeezing round as "failed to squeeze", so // that they're re-measured without squeezing later. - markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i); + markButtonsWithPendingSqueezeStatusAs( + LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions); // The current button doesn't fit, so there's no point in measuring further // buttons. @@ -426,7 +417,8 @@ public class SmartReplyView extends ViewGroup { // The current button fits, so mark all squeezed buttons as "successfully squeezed" // to prevent them from being un-squeezed in a subsequent squeezing round. - markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i); + markButtonsWithPendingSqueezeStatusAs( + LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions); } lp.show = true; @@ -445,6 +437,22 @@ public class SmartReplyView extends ViewGroup { mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec)); } + private List<View> filterActionsOrReplies(SmartButtonType buttonType) { + List<View> actions = new ArrayList<>(); + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) { + continue; + } + if (lp.buttonType == buttonType) { + actions.add(child); + } + } + return actions; + } + private void resetButtonsLayoutParams() { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -615,9 +623,9 @@ public class SmartReplyView extends ViewGroup { } } - private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) { - for (int i = 0; i <= maxChildIndex; i++) { - final View child = getChildAt(i); + private void markButtonsWithPendingSqueezeStatusAs( + int squeezeStatus, List<View> coveredChildren) { + for (View child : coveredChildren) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) { lp.squeezeStatus = squeezeStatus; @@ -712,6 +720,11 @@ public class SmartReplyView extends ViewGroup { return mActivityStarter; } + private enum SmartButtonType { + REPLY, + ACTION + } + @VisibleForTesting static class LayoutParams extends ViewGroup.LayoutParams { @@ -737,6 +750,7 @@ public class SmartReplyView extends ViewGroup { private boolean show = false; private int squeezeStatus = SQUEEZE_STATUS_NONE; + private SmartButtonType buttonType = SmartButtonType.REPLY; private LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index cd379c5f551f..4a69cd783a6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -42,7 +42,7 @@ public class TvStatusBar extends SystemUI implements Callbacks { public void start() { putComponent(TvStatusBar.class, this); CommandQueue commandQueue = getComponent(CommandQueue.class); - commandQueue.addCallbacks(this); + commandQueue.addCallback(this); int[] switches = new int[9]; ArrayList<IBinder> binders = new ArrayList<>(); ArrayList<String> iconSlots = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java index 0f7c9a462c0a..096ac3fcee1d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Assert.java +++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java @@ -18,19 +18,24 @@ package com.android.systemui.util; import android.os.Looper; +import com.android.internal.annotations.VisibleForTesting; + /** * Helper providing common assertions. */ public class Assert { + @VisibleForTesting + public static Looper sMainLooper = Looper.getMainLooper(); + public static void isMainThread() { - if (!Looper.getMainLooper().isCurrentThread()) { + if (!sMainLooper.isCurrentThread()) { throw new IllegalStateException("should be called from the main thread."); } } public static void isNotMainThread() { - if (Looper.getMainLooper().isCurrentThread()) { + if (sMainLooper.isCurrentThread()) { throw new IllegalStateException("should not be called from the main thread."); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 6812410c851c..490cdd5ce9bd 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -57,13 +57,13 @@ public class Utils { public void onViewAttachedToWindow(View v) { mView = v; SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class) - .addCallbacks(this); + .addCallback(this); } @Override public void onViewDetachedFromWindow(View v) { SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class) - .removeCallbacks(this); + .removeCallback(this); mView = null; } diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java deleted file mode 100644 index 9b616e00a72f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.volume; - -import android.animation.Animator; -import android.animation.AnimatorInflater; -import android.animation.AnimatorSet; -import android.annotation.DrawableRes; -import android.annotation.Nullable; -import android.app.Dialog; -import android.app.KeyguardManager; -import android.car.Car; -import android.car.CarNotConnectedException; -import android.car.media.CarAudioManager; -import android.car.media.ICarVolumeCallback; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.ServiceConnection; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseArray; -import android.util.Xml; -import android.view.ContextThemeWrapper; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; - -import androidx.car.widget.ListItem; -import androidx.car.widget.ListItemAdapter; -import androidx.car.widget.ListItemAdapter.BackgroundStyle; -import androidx.car.widget.ListItemProvider.ListProvider; -import androidx.car.widget.PagedListView; -import androidx.car.widget.SeekbarListItem; - -import com.android.systemui.R; -import com.android.systemui.plugins.VolumeDialog; - -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Car version of the volume dialog. - * - * Methods ending in "H" must be called on the (ui) handler. - */ -public class CarVolumeDialogImpl implements VolumeDialog { - private static final String TAG = Util.logTag(CarVolumeDialogImpl.class); - - private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; - private static final String XML_TAG_VOLUME_ITEM = "item"; - private static final int HOVERING_TIMEOUT = 16000; - private static final int NORMAL_TIMEOUT = 3000; - private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250; - private static final int DISMISS_DELAY_IN_MILLIS = 50; - private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100; - - private final Context mContext; - private final H mHandler = new H(); - - private Window mWindow; - private CustomDialog mDialog; - private PagedListView mListView; - private ListItemAdapter mPagedListAdapter; - // All the volume items. - private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>(); - // Available volume items in car audio manager. - private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>(); - // Volume items in the PagedListView. - private final List<ListItem> mVolumeLineItems = new ArrayList<>(); - private final KeyguardManager mKeyguard; - - private Car mCar; - private CarAudioManager mCarAudioManager; - - private boolean mHovering; - private boolean mShowing; - private boolean mExpanded; - - public CarVolumeDialogImpl(Context context) { - mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); - mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mCar = Car.createCar(mContext, mServiceConnection); - } - - public void init(int windowType, Callback callback) { - initDialog(); - - mCar.connect(); - } - - @Override - public void destroy() { - mHandler.removeCallbacksAndMessages(null); - - cleanupAudioManager(); - // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup - // audio manager beforehand. - mCar.disconnect(); - } - - private void initDialog() { - loadAudioUsageItems(); - mVolumeLineItems.clear(); - mDialog = new CustomDialog(mContext); - - mHovering = false; - mShowing = false; - mExpanded = false; - mWindow = mDialog.getWindow(); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); - mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); - mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast); - final WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.format = PixelFormat.TRANSLUCENT; - lp.setTitle(VolumeDialogImpl.class.getSimpleName()); - lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; - lp.windowAnimations = -1; - mWindow.setAttributes(lp); - mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - mDialog.setCanceledOnTouchOutside(true); - mDialog.setContentView(R.layout.car_volume_dialog); - mDialog.setOnShowListener(dialog -> { - mListView.setTranslationY(-mListView.getHeight()); - mListView.setAlpha(0); - mListView.animate() - .alpha(1) - .translationY(0) - .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) - .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) - .start(); - }); - mListView = (PagedListView) mWindow.findViewById(R.id.volume_list); - mListView.setOnHoverListener((v, event) -> { - int action = event.getActionMasked(); - mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) - || (action == MotionEvent.ACTION_HOVER_MOVE); - rescheduleTimeoutH(); - return true; - }); - - mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems), - BackgroundStyle.PANEL); - mListView.setAdapter(mPagedListAdapter); - mListView.setMaxPages(PagedListView.UNLIMITED_PAGES); - } - - public void show(int reason) { - mHandler.obtainMessage(H.SHOW, reason).sendToTarget(); - } - - public void dismiss(int reason) { - mHandler.obtainMessage(H.DISMISS, reason).sendToTarget(); - } - - private void showH(int reason) { - if (D.BUG) { - Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]); - } - - mHandler.removeMessages(H.SHOW); - mHandler.removeMessages(H.DISMISS); - rescheduleTimeoutH(); - // Refresh the data set before showing. - mPagedListAdapter.notifyDataSetChanged(); - if (mShowing) { - return; - } - mShowing = true; - - mDialog.show(); - Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); - } - - protected void rescheduleTimeoutH() { - mHandler.removeMessages(H.DISMISS); - final int timeout = computeTimeoutH(); - mHandler.sendMessageDelayed(mHandler - .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout); - - if (D.BUG) { - Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); - } - } - - private int computeTimeoutH() { - return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT; - } - - protected void dismissH(int reason) { - if (D.BUG) { - Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]); - } - - mHandler.removeMessages(H.DISMISS); - mHandler.removeMessages(H.SHOW); - if (!mShowing) { - return; - } - - mListView.animate().cancel(); - - mListView.setTranslationY(0); - mListView.setAlpha(1); - mListView.animate() - .alpha(0) - .translationY(-mListView.getHeight()) - .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) - .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) - .withEndAction(() -> mHandler.postDelayed(() -> { - if (D.BUG) { - Log.d(TAG, "mDialog.dismiss()"); - } - mDialog.dismiss(); - mShowing = false; - }, DISMISS_DELAY_IN_MILLIS)) - .start(); - - Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason); - } - - public void dump(PrintWriter writer) { - writer.println(VolumeDialogImpl.class.getSimpleName() + " state:"); - writer.print(" mShowing: "); writer.println(mShowing); - } - - private void loadAudioUsageItems() { - try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) { - AttributeSet attrs = Xml.asAttributeSet(parser); - int type; - // Traverse to the first start tag - while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT - && type != XmlResourceParser.START_TAG) { - } - - if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) { - throw new RuntimeException("Meta-data does not start with carVolumeItems tag"); - } - int outerDepth = parser.getDepth(); - int rank = 0; - while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT - && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlResourceParser.END_TAG) { - continue; - } - if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { - TypedArray item = mContext.getResources().obtainAttributes( - attrs, R.styleable.carVolumeItems_item); - int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); - if (usage >= 0) { - VolumeItem volumeItem = new VolumeItem(); - volumeItem.usage = usage; - volumeItem.rank = rank; - volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0); - mVolumeItems.put(usage, volumeItem); - rank++; - } - item.recycle(); - } - } - } catch (XmlPullParserException | IOException e) { - Log.e(TAG, "Error parsing volume groups configuration", e); - } - } - - private VolumeItem getVolumeItemForUsages(int[] usages) { - int rank = Integer.MAX_VALUE; - VolumeItem result = null; - for (int usage : usages) { - VolumeItem volumeItem = mVolumeItems.get(usage); - if (volumeItem.rank < rank) { - rank = volumeItem.rank; - result = volumeItem; - } - } - return result; - } - - private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; - } - - private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupMaxVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; - } - - private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId, - int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) { - SeekbarListItem listItem = new SeekbarListItem(mContext); - listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId)); - int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); - int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); - listItem.setProgress(progress); - listItem.setOnSeekBarChangeListener( - new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); - Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); - primaryIcon.mutate().setTint(color); - listItem.setPrimaryActionIcon(primaryIcon); - if (supplementalIconId != 0) { - Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); - supplementalIcon.mutate().setTint(color); - listItem.setSupplementalIcon(supplementalIcon, true); - listItem.setSupplementalIconListener(supplementalIconOnClickListener); - } else { - listItem.setSupplementalEmptyIcon(true); - listItem.setSupplementalIconListener(null); - } - - mVolumeLineItems.add(listItem); - volumeItem.listItem = listItem; - volumeItem.progress = progress; - return listItem; - } - - private VolumeItem findVolumeItem(SeekbarListItem targetItem) { - for (int i = 0; i < mVolumeItems.size(); ++i) { - VolumeItem volumeItem = mVolumeItems.valueAt(i); - if (volumeItem.listItem == targetItem) { - return volumeItem; - } - } - return null; - } - - private void cleanupAudioManager() { - try { - mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder()); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - mVolumeLineItems.clear(); - mCarAudioManager = null; - } - - private final class H extends Handler { - private static final int SHOW = 1; - private static final int DISMISS = 2; - - public H() { - super(Looper.getMainLooper()); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case SHOW: - showH(msg.arg1); - break; - case DISMISS: - dismissH(msg.arg1); - break; - default: - } - } - } - - private final class CustomDialog extends Dialog implements DialogInterface { - public CustomDialog(Context context) { - super(context, com.android.systemui.R.style.qs_theme); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - rescheduleTimeoutH(); - return super.dispatchTouchEvent(ev); - } - - @Override - protected void onStart() { - super.setCanceledOnTouchOutside(true); - super.onStart(); - } - - @Override - protected void onStop() { - super.onStop(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (isShowing()) { - if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { - mHandler.obtainMessage( - H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget(); - return true; - } - } - return false; - } - } - - private final class ExpandIconListener implements View.OnClickListener { - @Override - public void onClick(final View v) { - mExpanded = !mExpanded; - Animator inAnimator; - if (mExpanded) { - for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { - // Adding the items which are not coming from the default item. - VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - if (volumeItem.defaultItem) { - // Set progress here due to the progress of seekbar may not be updated. - volumeItem.listItem.setProgress(volumeItem.progress); - } else { - addSeekbarListItem(volumeItem, groupId, 0, null); - } - } - inAnimator = AnimatorInflater.loadAnimator( - mContext, R.anim.car_arrow_fade_in_rotate_up); - } else { - // Only keeping the default stream if it is not expended. - Iterator itr = mVolumeLineItems.iterator(); - while (itr.hasNext()) { - SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next(); - VolumeItem volumeItem = findVolumeItem(seekbarListItem); - if (!volumeItem.defaultItem) { - itr.remove(); - } else { - // Set progress here due to the progress of seekbar may not be updated. - seekbarListItem.setProgress(volumeItem.progress); - } - } - inAnimator = AnimatorInflater.loadAnimator( - mContext, R.anim.car_arrow_fade_in_rotate_down); - } - - Animator outAnimator = AnimatorInflater.loadAnimator( - mContext, R.anim.car_arrow_fade_out); - inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS); - AnimatorSet animators = new AnimatorSet(); - animators.playTogether(outAnimator, inAnimator); - animators.setTarget(v); - animators.start(); - mPagedListAdapter.notifyDataSetChanged(); - } - } - - private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener { - private final int mVolumeGroupId; - private final CarAudioManager mCarAudioManager; - - private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) { - mVolumeGroupId = volumeGroupId; - mCarAudioManager = carAudioManager; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (!fromUser) { - // For instance, if this event is originated from AudioService, - // we can ignore it as it has already been handled and doesn't need to be - // sent back down again. - return; - } - try { - if (mCarAudioManager == null) { - Log.w(TAG, "Ignoring volume change event because the car isn't connected"); - return; - } - mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; - mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - } - - private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() { - @Override - public void onGroupVolumeChanged(int groupId, int flags) { - VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - int value = getSeekbarValue(mCarAudioManager, groupId); - // Do not update the progress if it is the same as before. When car audio manager sets its - // group volume caused by the seekbar progress changed, it also triggers this callback. - // Updating the seekbar at the same time could block the continuous seeking. - if (value != volumeItem.progress) { - volumeItem.listItem.setProgress(value); - volumeItem.progress = value; - } - if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { - show(Events.SHOW_REASON_VOLUME_CHANGED); - } - } - - @Override - public void onMasterMuteChanged(int flags) { - // ignored - } - }; - - private final ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - try { - mExpanded = false; - mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); - int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); - // Populates volume slider items from volume groups to UI. - for (int groupId = 0; groupId < volumeGroupCount; groupId++) { - VolumeItem volumeItem = getVolumeItemForUsages( - mCarAudioManager.getUsagesForVolumeGroupId(groupId)); - mAvailableVolumeItems.add(volumeItem); - // The first one is the default item. - if (groupId == 0) { - volumeItem.defaultItem = true; - addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down, - new ExpandIconListener()); - } - } - - // If list is already initiated, update its content. - if (mPagedListAdapter != null) { - mPagedListAdapter.notifyDataSetChanged(); - } - mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder()); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - } - - /** - * This does not get called when service is properly disconnected. - * So we need to also handle cleanups in destroy(). - */ - @Override - public void onServiceDisconnected(ComponentName name) { - cleanupAudioManager(); - } - }; - - /** - * Wrapper class which contains information of each volume group. - */ - private static class VolumeItem { - private @AudioAttributes.AttributeUsage int usage; - private int rank; - private boolean defaultItem = false; - private @DrawableRes int icon; - private SeekbarListItem listItem; - private int progress; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index 2861dffe5460..080567739be7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -19,12 +19,10 @@ package com.android.systemui.volume; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.media.AudioManager; import android.media.VolumePolicy; import android.os.Bundle; -import android.os.Handler; import android.view.WindowManager.LayoutParams; import com.android.settingslib.applications.InterestingConfigChanges; @@ -57,7 +55,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false; private final SystemUI mSysui; - private final Context mContext; + protected final Context mContext; private final VolumeDialogControllerImpl mController; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE @@ -70,7 +68,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna 400 // vibrateToSilentDebounce ); - public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) { + public VolumeDialogComponent(SystemUI sysui, Context context) { mSysui = sysui; mContext = context; mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class); @@ -81,7 +79,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class) .withPlugin(VolumeDialog.class) .withDefault(this::createDefault) - .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault) .withCallback(dialog -> { if (mDialog != null) { mDialog.destroy(); @@ -94,7 +91,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna VOLUME_SILENT_DO_NOT_DISTURB); } - private VolumeDialog createDefault() { + protected VolumeDialog createDefault() { VolumeDialogImpl impl = new VolumeDialogImpl(mContext); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); @@ -102,10 +99,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna return impl; } - private VolumeDialog createCarDefault() { - return new CarVolumeDialogImpl(mContext); - } - @Override public void onTuningChanged(String key, String newValue) { if (VOLUME_DOWN_SILENT.equals(key)) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index e4f37decdf35..f8cf79322b40 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -22,6 +22,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.SystemUIFactory; import com.android.systemui.qs.tiles.DndTile; import java.io.FileDescriptor; @@ -43,7 +44,9 @@ public class VolumeUI extends SystemUI { mContext.getResources().getBoolean(R.bool.enable_safety_warning); mEnabled = enableVolumeUi || enableSafetyWarning; if (!mEnabled) return; - mVolumeComponent = new VolumeDialogComponent(this, mContext, null); + + mVolumeComponent = SystemUIFactory.getInstance() + .createVolumeDialogComponent(this, mContext); mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning); putComponent(VolumeComponent.class, getVolumeComponent()); setDefaultVolumeController(); diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index b32bf99ad53d..83ec33c69629 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -38,8 +38,6 @@ LOCAL_JAVA_LIBRARIES := \ android.test.runner \ telephony-common \ android.test.base \ - android.car \ - android.car.userlib LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 9cbe4152b5c4..7ca54231fe7b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -51,7 +51,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @RunWith(AndroidTestingRunner.class) public class KeyguardClockSwitchTest extends SysuiTestCase { private PluginManager mPluginManager; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java index 359832f7a542..58870e4acbd0 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java @@ -38,7 +38,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class KeyguardPinBasedInputViewTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java index b98ce39f5ed3..77895c97051c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java @@ -19,9 +19,14 @@ import android.graphics.Color; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; +import androidx.slice.SliceProvider; +import androidx.slice.SliceSpecs; +import androidx.slice.builders.ListBuilder; + import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.KeyguardSliceProvider; @@ -34,12 +39,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; -import androidx.slice.SliceProvider; -import androidx.slice.SliceSpecs; -import androidx.slice.builders.ListBuilder; - @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @RunWith(AndroidTestingRunner.class) public class KeyguardSliceViewTest extends SysuiTestCase { private KeyguardSliceView mKeyguardSliceView; @@ -47,6 +48,7 @@ public class KeyguardSliceViewTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext()) .inflate(R.layout.keyguard_status_area, null); mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java index 9e96df2c30cf..3582ab010413 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java @@ -20,10 +20,12 @@ import static org.mockito.Mockito.verify; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.Assert; import org.junit.Before; import org.junit.Test; @@ -32,7 +34,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @RunWith(AndroidTestingRunner.class) public class KeyguardStatusViewTest extends SysuiTestCase { @@ -45,6 +47,7 @@ public class KeyguardStatusViewTest extends SysuiTestCase { @Before public void setUp() { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); LayoutInflater layoutInflater = LayoutInflater.from(getContext()); mKeyguardStatusView = (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java index c180ff8fd30c..948e0014a451 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java @@ -16,29 +16,29 @@ package com.android.systemui; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import android.animation.ObjectAnimator; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.util.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class ExpandHelperTest extends SysuiTestCase { private ExpandableNotificationRow mRow; @@ -47,6 +47,7 @@ public class ExpandHelperTest extends SysuiTestCase { @Before public void setUp() throws Exception { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); Context context = getContext(); mRow = new NotificationTestHelper(context).createRow(); mCallback = mock(ExpandHelper.Callback.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index a58bc8548bd4..9c5a59243d05 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -29,6 +29,7 @@ import android.testing.LeakCheck; import android.util.Log; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.util.Assert; import org.junit.After; import org.junit.Before; @@ -78,6 +79,8 @@ public abstract class SysuiTestCase { public void SysuiTeardown() { InstrumentationRegistry.registerInstance(mRealInstrumentation, InstrumentationRegistry.getArguments()); + // Reset the assert's main looper. + Assert.sMainLooper = Looper.getMainLooper(); } protected LeakCheck getLeakCheck() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 368c814f8e0a..e1c481e4ef28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -28,8 +28,8 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -40,14 +40,12 @@ import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; -import android.view.Display; +import android.testing.UiThreadTest; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.wakelock.WakeLockFake; -import android.testing.UiThreadTest; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 5c8336c8dee1..31fc625d34dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -48,7 +48,7 @@ import org.junit.runner.RunWith; @SmallTest @Ignore("failing") @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class DozeTriggersTest extends SysuiTestCase { private DozeTriggers mTriggers; private DozeMachine mMachine; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 095395185f86..c1c80ce4a70a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -55,7 +55,7 @@ import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class KeyguardSliceProviderTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 4e24354d9878..bc7d9836d6f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -18,39 +18,32 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import android.app.FragmentController; -import android.app.FragmentManagerNonConfig; +import android.content.Context; import android.os.Looper; -import android.support.test.filters.FlakyTest; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.LayoutInflaterBuilder; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; +import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; import com.android.keyguard.CarrierText; import com.android.systemui.Dependency; import com.android.systemui.R; - -import android.os.Parcelable; -import android.support.test.filters.SmallTest; -import android.testing.AndroidTestingRunner; - import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.UserSwitcherController; -import android.testing.LayoutInflaterBuilder; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import android.content.Context; -import android.view.View; -import android.widget.FrameLayout; - @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @SmallTest @Ignore public class QSFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 33b347a66b33..fd31013db429 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.UserInfo; -import android.os.Looper; import android.os.UserManager; import android.provider.Settings; import android.test.suitebuilder.annotation.SmallTest; @@ -58,7 +57,7 @@ import org.mockito.Mockito; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class QSSecurityFooterTest extends SysuiTestCase { private final String MANAGING_ORGANIZATION = "organization"; @@ -76,7 +75,8 @@ public class QSSecurityFooterTest extends SysuiTestCase { @Before public void setUp() { mDependency.injectTestDependency(SecurityController.class, mSecurityController); - mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper()); + mDependency.injectTestDependency(Dependency.BG_LOOPER, + TestableLooper.get(this).getLooper()); mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, new LayoutInflaterBuilder(mContext) .replace("ImageView", TestableImageView.class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java new file mode 100644 index 000000000000..78700b88d4bc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + + +import static junit.framework.TestCase.assertFalse; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class QSTileHostTest extends SysuiTestCase { + + @Test + public void testLoadTileSpecs_emptySetting() { + List<String> tiles = QSTileHost.loadTileSpecs(mContext, ""); + assertFalse(tiles.isEmpty()); + } + + @Test + public void testLoadTileSpecs_nullSetting() { + List<String> tiles = QSTileHost.loadTileSpecs(mContext, null); + assertFalse(tiles.isEmpty()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java deleted file mode 100644 index f89a93264b0c..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java +++ /dev/null @@ -1,89 +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.systemui.qs.car; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.support.test.filters.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.LayoutInflaterBuilder; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.keyguard.CarrierText; -import com.android.systemui.Dependency; -import com.android.systemui.SysuiBaseFragmentTest; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.policy.Clock; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for {@link CarQSFragment}. - */ -@RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) -@SmallTest -@Ignore -public class CarQsFragmentTest extends SysuiBaseFragmentTest { - public CarQsFragmentTest() { - super(CarQSFragment.class); - } - - @Before - public void initDependencies() { - mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, - new LayoutInflaterBuilder(mContext) - .replace("com.android.systemui.statusbar.policy.SplitClockView", - FrameLayout.class) - .replace("TextClock", View.class) - .replace(CarrierText.class, View.class) - .replace(Clock.class, View.class) - .build()); - mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class)); - mDependency.injectTestDependency(Dependency.BG_LOOPER, - TestableLooper.get(this).getLooper()); - } - - @Test - @Ignore("Flaky") - public void testLayoutInflation() { - CarQSFragment fragment = (CarQSFragment) mFragment; - mFragments.dispatchResume(); - - assertNotNull(fragment.getHeader()); - assertNotNull(fragment.getFooter()); - } - - @Test - @Ignore("Flaky") - public void testListening() { - CarQSFragment qs = (CarQSFragment) mFragment; - mFragments.dispatchResume(); - processAllMessages(); - - qs.setListening(true); - processAllMessages(); - - qs.setListening(false); - processAllMessages(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index c016a851010a..c6597b9bd534 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -24,16 +24,15 @@ import android.content.ComponentName; import android.os.Looper; import android.service.quicksettings.Tile; import android.test.suitebuilder.annotation.SmallTest; - import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; + import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BluetoothController; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -45,7 +44,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class TileServicesTest extends SysuiTestCase { private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 8ae3cd8d6acd..a04e57bb4990 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -14,6 +14,8 @@ package com.android.systemui.statusbar; +import static android.view.Display.DEFAULT_DISPLAY; + import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -42,7 +44,7 @@ public class CommandQueueTest extends SysuiTestCase { public void setup() { mCommandQueue = new CommandQueue(); mCallbacks = mock(Callbacks.class); - mCommandQueue.addCallbacks(mCallbacks); + mCommandQueue.addCallback(mCallbacks); verify(mCallbacks).disable(eq(0), eq(0), eq(false)); } @@ -64,11 +66,12 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).removeIcon(eq(slot)); } + // TODO(b/117478341): add test case for multi-display @Test public void testDisable() { int state1 = 14; int state2 = 42; - mCommandQueue.disable(state1, state2); + mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2); waitForIdleSync(); verify(mCallbacks).disable(eq(state1), eq(state2), eq(true)); } @@ -95,24 +98,27 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).animateExpandSettingsPanel(eq(panel)); } + // TODO(b/117478341): add test case for multi-display @Test public void testSetSystemUiVisibility() { Rect r = new Rect(); - mCommandQueue.setSystemUiVisibility(1, 2, 3, 4, null, r); + mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r); waitForIdleSync(); verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r)); } + // TODO(b/117478341): add test case for multi-display @Test public void testTopAppWindowChanged() { - mCommandQueue.topAppWindowChanged(true); + mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true); waitForIdleSync(); verify(mCallbacks).topAppWindowChanged(eq(true)); } + // TODO(b/117478341): add test case for multi-display @Test public void testShowImeButton() { - mCommandQueue.setImeWindowStatus(null, 1, 2, true); + mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true); waitForIdleSync(); verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true)); } @@ -166,9 +172,10 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1)); } + // TODO(b/117478341): add test case for multi-display @Test public void testSetWindowState() { - mCommandQueue.setWindowState(1, 2); + mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2); waitForIdleSync(); verify(mCallbacks).setWindowState(eq(1), eq(2)); } @@ -180,30 +187,34 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).showScreenPinningRequest(eq(1)); } + // TODO(b/117478341): add test case for multi-display @Test public void testAppTransitionPending() { - mCommandQueue.appTransitionPending(); + mCommandQueue.appTransitionPending(DEFAULT_DISPLAY); waitForIdleSync(); verify(mCallbacks).appTransitionPending(eq(false)); } + // TODO(b/117478341): add test case for multi-display @Test public void testAppTransitionCancelled() { - mCommandQueue.appTransitionCancelled(); + mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY); waitForIdleSync(); verify(mCallbacks).appTransitionCancelled(); } + // TODO(b/117478341): add test case for multi-display @Test public void testAppTransitionStarting() { - mCommandQueue.appTransitionStarting(1, 2); + mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2); waitForIdleSync(); verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false)); } + // TODO(b/117478341): add test case for multi-display @Test public void testAppTransitionFinished() { - mCommandQueue.appTransitionFinished(); + mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY); waitForIdleSync(); verify(mCallbacks).appTransitionFinished(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java index 2e280d336aab..9449e297fcc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations; */ @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper @Ignore("b/118400112") public class NonPhoneDependencyTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index 63ececbe2994..65c04fe4bcd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -16,15 +16,12 @@ package com.android.systemui.statusbar; -import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; import android.os.Handler; -import android.os.Looper; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -45,7 +42,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class NotificationListenerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 72d6cd8eaeea..520a927d1ab0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.util.Assert; import com.google.android.collect.Lists; @@ -60,7 +61,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; @Mock private NotificationData mNotificationData; @@ -79,6 +80,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + Assert.sMainLooper = TestableLooper.get(this).getLooper(); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java index b7aa21b86d33..db2c8780e783 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java @@ -19,15 +19,15 @@ package com.android.systemui.statusbar.notification; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.widget.FrameLayout; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import org.junit.Assert; import org.junit.Before; @@ -36,7 +36,7 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class AboveShelfObserverTest extends SysuiTestCase { private AboveShelfObserver mObserver; @@ -46,6 +46,7 @@ public class AboveShelfObserverTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); mNotificationTestHelper = new NotificationTestHelper(getContext()); mHostLayout = new FrameLayout(getContext()); mObserver = new AboveShelfObserver(mHostLayout); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java index f94ba95999bf..8e88ed0556bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java @@ -53,6 +53,7 @@ import android.service.notification.StatusBarNotification; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArraySet; @@ -78,7 +79,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class NotificationDataTest extends SysuiTestCase { private static final int UID_NORMAL = 123; @@ -101,6 +102,7 @@ public class NotificationDataTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java index 63d1e8dbc954..24aa772e2fc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java @@ -17,28 +17,29 @@ package com.android.systemui.statusbar.notification; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; +import com.android.systemui.util.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidTestingRunner.class) @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class NotificationViewWrapperTest extends SysuiTestCase { @Test public void constructor_doesntUseViewContext() throws Exception { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); new TestableNotificationViewWrapper(mContext, new View(mContext), new NotificationTestHelper(getContext()).createRow()); @@ -50,4 +51,4 @@ public class NotificationViewWrapperTest extends SysuiTestCase { super(ctx, view, row); } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index 3710fa833d50..512acd073a84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -37,13 +37,13 @@ import android.testing.TestableLooper; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; - -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; + import com.google.android.collect.Lists; import org.junit.Before; @@ -58,7 +58,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class NotificationLoggerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 2da72e7858c8..6d3553912701 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -40,6 +40,7 @@ import android.app.AppOpsManager; import android.app.NotificationChannel; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArraySet; import android.view.NotificationHeaderView; @@ -64,7 +65,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class ExpandableNotificationRowTest extends SysuiTestCase { private ExpandableNotificationRow mGroupRow; @@ -77,6 +78,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); mNotificationTestHelper = new NotificationTestHelper(mContext); mGroupRow = mNotificationTestHelper.createGroup(); mGroupRow.setHeadsUpAnimatingAwayListener( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java index 4efab5385c0a..669b98e1b279 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java @@ -16,10 +16,18 @@ package com.android.systemui.statusbar.notification.row; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.NotificationTestHelper; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.support.test.filters.FlakyTest; @@ -28,30 +36,24 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.util.Assert; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - /** * Tests for {@link NotificationBlockingHelperManager}. */ @SmallTest @FlakyTest @org.junit.runner.RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class NotificationBlockingHelperManagerTest extends SysuiTestCase { private NotificationBlockingHelperManager mBlockingHelperManager; @@ -65,6 +67,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Before public void setUp() { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); when(mGutsManager.openGuts( any(View.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 766c5d2377c6..8966aca3069c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -64,6 +64,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.Assert; import org.junit.Before; import org.junit.Rule; @@ -79,7 +80,7 @@ import org.mockito.junit.MockitoRule; */ @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class NotificationGutsManagerTest extends SysuiTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; @@ -101,6 +102,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Before public void setUp() { mTestableLooper = TestableLooper.get(this); + Assert.sMainLooper = TestableLooper.get(this).getLooper(); mDependency.injectTestDependency(DeviceProvisionedController.class, mDeviceProvisionedController); mHandler = Handler.createAsync(mTestableLooper.getLooper()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index 906e718f19d9..e4d019656072 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -25,7 +25,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Notification; +import android.app.NotificationChannel; import android.service.notification.StatusBarNotification; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -35,6 +35,7 @@ import android.testing.ViewUtils; import android.view.ViewGroup; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; @@ -43,19 +44,26 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; @RunWith(AndroidTestingRunner.class) -@RunWithLooper() +@RunWithLooper @SmallTest public class NotificationMenuRowTest extends LeakCheckedTest { + private ExpandableNotificationRow mRow; + @Before public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); + mRow = mock(ExpandableNotificationRow.class); + NotificationData.Entry entry = new NotificationData.Entry( + mock(StatusBarNotification.class)); + entry.channel = mock(NotificationChannel.class); + when(mRow.getEntry()).thenReturn(entry); } @Test public void testAttachDetach() { NotificationMenuRowPlugin row = new NotificationMenuRow(mContext); - row.createMenu(null, null); + row.createMenu(mRow, null); ViewUtils.attachView(row.getMenuView()); TestableLooper.get(this).processAllMessages(); ViewUtils.detachView(row.getMenuView()); @@ -65,9 +73,9 @@ public class NotificationMenuRowTest extends LeakCheckedTest { @Test public void testRecreateMenu() { NotificationMenuRowPlugin row = new NotificationMenuRow(mContext); - row.createMenu(null, null); + row.createMenu(mRow, null); assertTrue(row.getMenuView() != null); - row.createMenu(null, null); + row.createMenu(mRow, null); assertTrue(row.getMenuView() != null); } @@ -81,12 +89,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { @Test public void testNoAppOpsInSlowSwipe() { NotificationMenuRow row = new NotificationMenuRow(mContext); - Notification n = mock(Notification.class); - StatusBarNotification sbn = mock(StatusBarNotification.class); - when(sbn.getNotification()).thenReturn(n); - ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class); - when(parent.getStatusBarNotification()).thenReturn(sbn); - row.createMenu(parent, null); + row.createMenu(mRow, null); ViewGroup container = (ViewGroup) row.getMenuView(); // one for snooze and one for noti blocking diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java index 4b94a2523cfe..fed66af07bcc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.widget.RemoteViews; @@ -34,13 +35,14 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class NotificationCustomViewWrapperTest extends SysuiTestCase { private ExpandableNotificationRow mRow; @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); mRow = new NotificationTestHelper(mContext).createRow(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index 272845396e27..bbafb4e4a211 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -18,13 +18,14 @@ package com.android.systemui.statusbar.notification.stack; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.NotificationHeaderView; import android.view.View; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import org.junit.Assert; import org.junit.Before; @@ -33,16 +34,16 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class NotificationChildrenContainerTest extends SysuiTestCase { private ExpandableNotificationRow mGroup; - private int mId; private NotificationTestHelper mNotificationTestHelper; private NotificationChildrenContainer mChildrenContainer; @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); mNotificationTestHelper = new NotificationTestHelper(mContext); mGroup = mNotificationTestHelper.createGroup(); mChildrenContainer = mGroup.getChildrenContainer(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index 662e016b77ff..8ae7d52a5da5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.SysuiTestCase; @@ -41,7 +42,7 @@ import java.util.HashSet; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class NotificationRoundnessManagerTest extends SysuiTestCase { private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager(); @@ -52,6 +53,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); mFirst = testHelper.createRow(); mFirst.setHeadsUpAnimatingAwayListener(animatingAway 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 c99e766ac697..4f6329cb0c57 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 @@ -23,18 +23,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.widget.TextView; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; import org.junit.Assert; import org.junit.Before; @@ -43,7 +43,7 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private final NotificationStackScrollLayout mStackScroller = @@ -58,6 +58,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); mFirst = testHelper.createRow(); mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher); 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 7f8668fa064c..f7a95c50fee8 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 @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.content.res.ColorStateList; import android.graphics.Color; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -35,7 +36,6 @@ import android.testing.TestableLooper; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; -import android.content.res.ColorStateList; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardHostView; @@ -56,7 +56,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class KeyguardBouncerTest extends SysuiTestCase { @Mock @@ -78,6 +78,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Before public void setup() { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); DejankUtils.setImmediate(true); final ViewGroup container = new FrameLayout(getContext()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 31014b8a62e9..27ed9c5a14c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -30,7 +30,7 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final int SCREEN_HEIGHT = 2000; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java index 54291536093c..c5875554b073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java @@ -20,21 +20,20 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; -import android.view.View; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class KeyguardPresentationTest extends SysuiTestCase { @Test public void testInflation_doesntCrash() { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); LayoutInflater inflater = LayoutInflater.from(getContext()); inflater.inflate(R.layout.keyguard_presentation, null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java index 8fc15b247ab9..cdaa2420186f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java @@ -29,7 +29,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -38,11 +37,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import com.android.systemui.R; -import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.shared.recents.IOverviewProxy; -import com.android.systemui.SysuiTestCase; - import android.content.Context; import android.content.res.Resources; import android.support.test.filters.SmallTest; @@ -51,6 +45,11 @@ import android.testing.TestableLooper.RunWithLooper; import android.view.MotionEvent; import android.view.View; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.shared.recents.IOverviewProxy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,7 +58,7 @@ import org.mockito.MockitoAnnotations; /** atest QuickStepControllerTest */ @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @SmallTest public class QuickStepControllerTest extends SysuiTestCase { private QuickStepController mController; @@ -78,6 +77,7 @@ public class QuickStepControllerTest extends SysuiTestCase { mProxyService = mock(OverviewProxyService.class); mProxy = mock(IOverviewProxy.Stub.class); doReturn(mProxy).when(mProxyService).getProxy(); + doReturn(true).when(mProxyService).shouldShowSwipeUpUI(); mDependency.injectTestDependency(OverviewProxyService.class, mProxyService); mStatusBar = mock(StatusBar.class); @@ -106,6 +106,18 @@ public class QuickStepControllerTest extends SysuiTestCase { } @Test + public void testNoGesturesWhenSwipeUpDisabled() throws Exception { + doReturn(false).when(mProxyService).shouldShowSwipeUpUI(); + mController.setGestureActions(mockAction(true), null /* swipeDownAction */, + null /* swipeLeftAction */, null /* swipeRightAction */); + + MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1); + assertFalse(mController.onInterceptTouchEvent(ev)); + verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev); + assertNull(mController.getCurrentAction()); + } + + @Test public void testHasActionDetectGesturesTouchdown() throws Exception { MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index e9e8eb785d1a..c207feff26f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -72,6 +72,8 @@ import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.assist.AssistManager; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; @@ -123,6 +125,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private IStatusBarService mBarService; @Mock private IDreamManager mDreamManager; @Mock private ScrimController mScrimController; + @Mock private DozeScrimController mDozeScrimController; @Mock private ArrayList<Entry> mNotificationList; @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private NotificationData mNotificationData; @@ -211,7 +214,7 @@ public class StatusBarTest extends SysuiTestCase { mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class), mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class), mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class), - mock(DozeScrimController.class), mock(NotificationShelf.class), + mDozeScrimController, mock(NotificationShelf.class), mLockscreenUserManager, mCommandQueue, mNotificationPresenter, mock(BubbleController.class)); mStatusBar.mContext = mContext; @@ -570,7 +573,28 @@ public class StatusBarTest extends SysuiTestCase { } @Test + public void testPulseWhileDozing_updatesScrimController() { + mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); + mStatusBar.showKeyguardImpl(); + + // Keep track of callback to be able to stop the pulse + DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1]; + doAnswer(invocation -> { + pulseCallback[0] = invocation.getArgument(0); + return null; + }).when(mDozeScrimController).pulse(any(), anyInt()); + // Starting a pulse should change the scrim controller to the pulsing state + mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), + DozeLog.PULSE_REASON_NOTIFICATION); + verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any()); + + // Ending a pulse should take it back to keyguard state + pulseCallback[0].onPulseFinished(); + verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD)); + } + + @Test public void testSetState_changesIsFullScreenUserSwitcherState() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); assertFalse(mStatusBar.isFullScreenUserSwitcherState()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java index dcd531dc9eb7..090963be7ac7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java @@ -15,6 +15,7 @@ package com.android.systemui.statusbar.phone; import static junit.framework.Assert.assertTrue; + import static org.mockito.Matchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -35,7 +36,7 @@ import org.mockito.ArgumentCaptor; @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper @SmallTest public class SystemUIDialogTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 3164c0469c40..b3ac6be65d36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -32,9 +32,10 @@ import android.widget.ImageButton; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.util.Assert; import org.junit.After; import org.junit.Before; @@ -44,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper @SmallTest public class RemoteInputViewTest extends SysuiTestCase { @@ -60,6 +61,7 @@ public class RemoteInputViewTest extends SysuiTestCase { @Before public void setUp() throws Exception { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index df7aeab2ed38..506fa974944a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -801,4 +801,55 @@ public class SmartReplyViewTest extends SysuiTestCase { assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1)); assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2)); } + + @Test + public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() { + String[] choices = new String[] {"Reply"}; + String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"}; + + // All actions should be displayed as DOUBLE-line smart action buttons. + ViewGroup expectedView = buildExpectedView(new String[0], 2, + createActions(new String[] { + "Looooooong \nactioooon", "second \naction", "third \naction"})); + expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + + setSmartRepliesAndActions(choices, actions); + mView.measure( + MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST), + MeasureSpec.UNSPECIFIED); + + assertEqualMeasures(expectedView, mView); + // smart replies + assertReplyButtonHidden(mView.getChildAt(0)); + // smart actions + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1)); + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2)); + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3)); + } + + @Test + public void testMeasure_choicesAndActionsPrioritizeActions() { + String[] choices = new String[] {"Short", "longer reply"}; + String[] actions = new String[] {"Looooooong actioooon", "second action"}; + + // All actions should be displayed as DOUBLE-line smart action buttons. + ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2, + createActions(new String[] {"Looooooong \nactioooon", "second \naction"})); + expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + + setSmartRepliesAndActions(choices, actions); + mView.measure( + MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST), + MeasureSpec.UNSPECIFIED); + + Button firstAction = ((Button) mView.getChildAt(1)); + + assertEqualMeasures(expectedView, mView); + // smart replies + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0)); + assertReplyButtonHidden(mView.getChildAt(1)); + // smart actions + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2)); + assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java index 43942f72993e..ab9b0c979fd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java @@ -20,11 +20,13 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.animation.Animator; +import android.os.Looper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.Assert; import org.junit.Before; import org.junit.Test; @@ -33,7 +35,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class KeepAwakeAnimationListenerTest extends SysuiTestCase { @Mock WakeLock mWakeLock; @@ -41,6 +43,7 @@ public class KeepAwakeAnimationListenerTest extends SysuiTestCase { @Before public void setup() { + Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); KeepAwakeAnimationListener.sWakeLock = mWakeLock; mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext()); @@ -55,4 +58,10 @@ public class KeepAwakeAnimationListenerTest extends SysuiTestCase { mKeepAwakeAnimationListener.onAnimationEnd((Animator) null); verify(mWakeLock).release(); } + + @Test(expected = IllegalStateException.class) + public void initThrows_onNonMainThread() { + Assert.sMainLooper = Looper.getMainLooper(); + new KeepAwakeAnimationListener(getContext()); + } } diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk new file mode 100644 index 000000000000..d316fbd8907c --- /dev/null +++ b/packages/overlays/AccentColorBlackOverlay/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := AccentColorBlack +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := AccentColorBlackOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..3b99648d20aa --- /dev/null +++ b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml @@ -0,0 +1,25 @@ +<!-- +/** + * 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.color.black" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/> + + <application android:label="@string/accent_color_black_overlay" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml new file mode 100644 index 000000000000..5648f915490b --- /dev/null +++ b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml @@ -0,0 +1,22 @@ +<?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> + <color name="accent_device_default_light">#202020</color> + <color name="accent_device_default_dark">#FFFFFF</color> +</resources> diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml new file mode 100644 index 000000000000..baf09b11ac07 --- /dev/null +++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml @@ -0,0 +1,23 @@ +<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Black accent color name application label. [CHAR LIMIT=50] --> + <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string> +</resources> + diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk new file mode 100644 index 000000000000..afc42873a4d6 --- /dev/null +++ b/packages/overlays/AccentColorGreenOverlay/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := AccentColorGreen +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := AccentColorGreenOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..609d5be8a758 --- /dev/null +++ b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml @@ -0,0 +1,25 @@ +<!-- +/** + * 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.color.green" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/> + + <application android:label="@string/accent_color_green_overlay" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml new file mode 100644 index 000000000000..089f08c6bb19 --- /dev/null +++ b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml @@ -0,0 +1,22 @@ +<?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> + <color name="accent_device_default_light">#1B873B</color> + <color name="accent_device_default_dark">#84C188</color> +</resources> diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml new file mode 100644 index 000000000000..4de344cbb65e --- /dev/null +++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml @@ -0,0 +1,23 @@ +<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Green accent color name application label. [CHAR LIMIT=50] --> + <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string> +</resources> + diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk new file mode 100644 index 000000000000..336616921d71 --- /dev/null +++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := AccentColorPurple +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := AccentColorPurpleOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..497a35815554 --- /dev/null +++ b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml @@ -0,0 +1,25 @@ +<!-- +/** + * 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.color.purple" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/> + + <application android:label="@string/accent_color_purple_overlay" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml new file mode 100644 index 000000000000..7e34bac3d9fb --- /dev/null +++ b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml @@ -0,0 +1,22 @@ +<?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> + <color name="accent_device_default_light">#725AFF</color> + <color name="accent_device_default_dark">#B5A9FC</color> +</resources> diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml new file mode 100644 index 000000000000..d1eb95a940ac --- /dev/null +++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml @@ -0,0 +1,23 @@ +<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Purple accent color name application label. [CHAR LIMIT=50] --> + <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string> +</resources> + diff --git a/packages/overlays/AdaptiveIconChangeOverlay/Android.mk b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk new file mode 100644 index 000000000000..6e3b8cbbcf2f --- /dev/null +++ b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := SquareIcon +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := SquareIconOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..33da51047bf0 --- /dev/null +++ b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml @@ -0,0 +1,27 @@ +<!-- +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon.square" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="android.theme.customization.adaptive_icon_shape" + android:priority="1"/> + + <application android:label="@string/square_icon_overlay" android:hasCode="false"/> +</manifest> diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml index 8c760fc45e72..54623f5c9fb0 100644 --- a/packages/SystemUI/res/values/arrays_car.xml +++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -** -** Copyright 2015, The Android Open Source Project +/* +** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -16,15 +16,11 @@ ** limitations under the License. */ --> -<resources> - <!-- These should be overriden in an overlay. The default implementation is empty. - There needs to be correspondence per index between these arrays, which means that if there - isn't a longpress action associated with a shortcut item, put in an empty item to make - sure everything lines up. - --> - <array name="car_facet_icons" /> - <array name="car_facet_intent_uris" /> - <array name="car_facet_longpress_intent_uris" /> - <array name="car_facet_package_filters"/> - <array name="car_facet_category_filters"/> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. --> + <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string> + <!-- Flag indicating whether round icons should be parsed from the application manifest. --> + <bool name="config_useRoundIcon">false</bool> + </resources> + diff --git a/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml new file mode 100644 index 000000000000..64b7d0dc16b5 --- /dev/null +++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml @@ -0,0 +1,23 @@ +<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Square icon overlay [DO NOT TRANSLATE] --> + <string name="square_icon_overlay">Square Icons</string> + +</resources> diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 0da07ae52857..944ee3390150 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -70,10 +70,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; -import com.android.server.AbstractMasterSystemService; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; +import com.android.server.infra.AbstractMasterSystemService; import com.android.server.intelligence.IntelligenceManagerInternal; import java.io.FileDescriptor; diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 0df99d4b6642..1f229cd2a157 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -70,11 +70,11 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.server.AbstractPerUserSystemService; import com.android.server.LocalServices; import com.android.server.autofill.AutofillManagerService.AutofillCompatState; import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode; import com.android.server.autofill.ui.AutoFillUI; +import com.android.server.infra.AbstractPerUserSystemService; import java.io.PrintWriter; import java.util.ArrayList; @@ -190,6 +190,11 @@ final class AutofillManagerServiceImpl return mInfo.getServiceInfo(); } + @Override // from PerUserSystemService + protected String getDefaultComponentName() { + return getComponentNameFromSettings(); + } + @Nullable String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) { return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId); @@ -369,7 +374,7 @@ final class AutofillManagerServiceImpl final long identity = Binder.clearCallingIdentity(); try { - final String autoFillService = getComponentNameFromSettings(); + final String autoFillService = getComponentNameLocked(); final ComponentName componentName = serviceInfo.getComponentName(); if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) { mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF, diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index af6575954842..4b7d29025730 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -40,7 +40,7 @@ import android.service.autofill.SaveRequest; import android.text.format.DateUtils; import android.util.Slog; -import com.android.server.AbstractSinglePendingRequestRemoteService; +import com.android.server.infra.AbstractSinglePendingRequestRemoteService; final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4c645076eb95..fb64cb28619d 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -95,10 +95,10 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; -import com.android.server.AbstractRemoteService; import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.PendingUi; +import com.android.server.infra.AbstractRemoteService; import com.android.server.intelligence.IntelligenceManagerInternal; import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; @@ -2579,11 +2579,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + " when server returned null for session " + this.id); } + final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue(); + // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize // furgher AFM -> AFMS calls. // TODO(b/119638958): add CTS tests return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient, - mActivityToken, this.id, mCurrentViewId); + mActivityToken, this.id, mCurrentViewId, currentValue); } @GuardedBy("mLock") diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java new file mode 100644 index 000000000000..f356b4f102e2 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java @@ -0,0 +1,117 @@ +/* + * 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.backup.encryption.keys; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.IntDef; +import android.content.Context; +import android.security.keystore.recovery.InternalRecoveryServiceException; +import android.security.keystore.recovery.RecoveryController; +import android.util.Slog; + +import javax.crypto.SecretKey; + +/** + * Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a + * provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also + * synced with the recoverable key store, wrapped by the primary key. This allows them to be + * recovered on a user's subsequent device through providing their lock screen secret. + */ +public class RecoverableKeyStoreSecondaryKey { + private static final String TAG = "RecoverableKeyStoreSecondaryKey"; + + private final String mAlias; + private final SecretKey mSecretKey; + + /** + * A new instance. + * + * @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key + * store. + * @param secretKey The key. + */ + public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) { + mAlias = checkNotNull(alias); + mSecretKey = checkNotNull(secretKey); + } + + /** + * The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify + * wrapped tertiary keys on the backup server. + */ + public String getAlias() { + return mAlias; + } + + /** The secret key, to be used to wrap tertiary keys. */ + public SecretKey getSecretKey() { + return mSecretKey; + } + + /** + * The status of the key. i.e., whether it's been synced to remote trusted hardware. + * + * @param context The application context. + * @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}. + */ + public @Status int getStatus(Context context) { + try { + return getStatusInternal(context); + } catch (InternalRecoveryServiceException e) { + Slog.wtf(TAG, "Internal error getting recovery status", e); + // Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly + // attempt to reinitialize. + return Status.NOT_SYNCED; + } + } + + private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException { + int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias); + switch (status) { + case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE: + return Status.DESTROYED; + case RecoveryController.RECOVERY_STATUS_SYNCED: + return Status.SYNCED; + case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS: + return Status.NOT_SYNCED; + default: + // Throw an exception if we encounter a status that doesn't match any of the above. + throw new InternalRecoveryServiceException( + "Unexpected status from getRecoveryStatus: " + status); + } + } + + /** Status of a key in the recoverable key store. */ + @IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED}) + public @interface Status { + /** + * The key has not yet been synced to remote trusted hardware. This may be because the user + * has not yet unlocked their device. + */ + int NOT_SYNCED = 1; + + /** + * The key has been synced with remote trusted hardware. It should now be recoverable on + * another device. + */ + int SYNCED = 2; + + /** The key has been lost forever. This can occur if the user disables their lock screen. */ + int DESTROYED = 3; + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java new file mode 100644 index 000000000000..db5fe77478a5 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java @@ -0,0 +1,119 @@ +/* + * 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.backup.encryption.keys; + +import android.content.Context; +import android.security.keystore.recovery.InternalRecoveryServiceException; +import android.security.keystore.recovery.LockScreenRequiredException; +import android.security.keystore.recovery.RecoveryController; +import android.util.ByteStringUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.util.Optional; + +import javax.crypto.SecretKey; + +/** + * Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}. + * + * <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing + * recovery of keys on other devices owned by the user. + */ +public class RecoverableKeyStoreSecondaryKeyManager { + private static final String BACKUP_KEY_ALIAS_PREFIX = + "com.android.server.backup/recoverablekeystore/"; + private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128; + private static final int BITS_PER_BYTE = 8; + + /** A new instance. */ + public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) { + return new RecoverableKeyStoreSecondaryKeyManager( + RecoveryController.getInstance(context), new SecureRandom()); + } + + private final RecoveryController mRecoveryController; + private final SecureRandom mSecureRandom; + + @VisibleForTesting + public RecoverableKeyStoreSecondaryKeyManager( + RecoveryController recoveryController, SecureRandom secureRandom) { + mRecoveryController = recoveryController; + mSecureRandom = secureRandom; + } + + /** + * Generates a new recoverable key using the {@link RecoveryController}. + * + * @throws InternalRecoveryServiceException if an unexpected error occurred generating the key. + * @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is + * required to generate a recoverable key. + */ + public RecoverableKeyStoreSecondaryKey generate() + throws InternalRecoveryServiceException, LockScreenRequiredException, + UnrecoverableKeyException { + String alias = generateId(); + mRecoveryController.generateKey(alias); + SecretKey key = (SecretKey) mRecoveryController.getKey(alias); + if (key == null) { + throw new InternalRecoveryServiceException( + String.format( + "Generated key %s but could not get it back immediately afterwards.", + alias)); + } + return new RecoverableKeyStoreSecondaryKey(alias, key); + } + + /** + * Removes the secondary key. This means the key will no longer be recoverable. + * + * @param alias The alias of the key. + * @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error. + */ + public void remove(String alias) throws InternalRecoveryServiceException { + mRecoveryController.removeKey(alias); + } + + /** + * Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link + * RecoveryController}. Otherwise, {@link Optional#empty()}. + */ + public Optional<RecoverableKeyStoreSecondaryKey> get(String alias) + throws InternalRecoveryServiceException, UnrecoverableKeyException { + SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias); + return Optional.ofNullable(secretKey) + .map(key -> new RecoverableKeyStoreSecondaryKey(alias, key)); + } + + /** + * Generates a new key alias. This has more entropy than a UUID - it can be considered + * universally unique. + */ + private String generateId() { + byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE]; + mSecureRandom.nextBytes(id); + return BACKUP_KEY_ALIAS_PREFIX + ByteStringUtils.toHexString(id); + } + + /** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */ + public interface RecoverableKeyStoreSecondaryKeyManagerProvider { + /** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */ + RecoverableKeyStoreSecondaryKeyManager get(); + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java new file mode 100644 index 000000000000..ebf09dfd6ba6 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java @@ -0,0 +1,47 @@ +/* + * 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.backup.encryption.keys; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** 256-bit AES key generator. Each app should have its own separate AES key. */ +public class TertiaryKeyGenerator { + private static final int KEY_SIZE_BITS = 256; + private static final String KEY_ALGORITHM = "AES"; + + private final KeyGenerator mKeyGenerator; + + /** New instance generating keys using {@code secureRandom}. */ + public TertiaryKeyGenerator(SecureRandom secureRandom) { + try { + mKeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); + mKeyGenerator.init(KEY_SIZE_BITS, secureRandom); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException( + "Impossible condition: JCE thinks it does not support AES.", e); + } + } + + /** Generates a new random AES key. */ + public SecretKey generate() { + return mKeyGenerator.generateKey(); + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java new file mode 100644 index 000000000000..ec90f6c8c95e --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java @@ -0,0 +1,113 @@ +/* + * 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.backup.encryption.keys; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Locale; + +/** + * Tracks when a tertiary key rotation is due. + * + * <p>After a certain number of incremental backups, the device schedules a full backup, which will + * generate a new encryption key, effecting a key rotation. We should do this on a regular basis so + * that if a key does become compromised it has limited value to the attacker. + * + * <p>No additional synchronization of this class is provided. Only one instance should be used at + * any time. This should be fine as there should be no parallelism in backups. + */ +public class TertiaryKeyRotationTracker { + private static final int MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION = 31; + private static final String SHARED_PREFERENCES_NAME = "tertiary_key_rotation_tracker"; + + private static final String TAG = "TertiaryKeyRotationTracker"; + private static final boolean DEBUG = false; + + /** + * A new instance, using {@code context} to commit data to disk via {@link SharedPreferences}. + */ + public static TertiaryKeyRotationTracker getInstance(Context context) { + return new TertiaryKeyRotationTracker( + context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)); + } + + private final SharedPreferences mSharedPreferences; + + /** New instance, storing data in {@code mSharedPreferences}. */ + @VisibleForTesting + TertiaryKeyRotationTracker(SharedPreferences sharedPreferences) { + mSharedPreferences = sharedPreferences; + } + + /** + * Returns {@code true} if the given app is due having its key rotated. + * + * @param packageName The package name of the app. + */ + public boolean isKeyRotationDue(String packageName) { + return getBackupsSinceRotation(packageName) >= MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION; + } + + /** + * Records that an incremental backup has occurred. Each incremental backup brings the app + * closer to the time when its key should be rotated. + * + * @param packageName The package name of the app for which the backup occurred. + */ + public void recordBackup(String packageName) { + int backupsSinceRotation = getBackupsSinceRotation(packageName) + 1; + mSharedPreferences.edit().putInt(packageName, backupsSinceRotation).apply(); + if (DEBUG) { + Slog.d( + TAG, + String.format( + Locale.US, + "Incremental backup for %s. %d backups until key rotation.", + packageName, + Math.max( + 0, + MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION + - backupsSinceRotation))); + } + } + + /** + * Resets the rotation delay for the given app. Should be invoked after a key rotation. + * + * @param packageName Package name of the app whose key has rotated. + */ + public void resetCountdown(String packageName) { + mSharedPreferences.edit().putInt(packageName, 0).apply(); + } + + /** Marks all enrolled packages for key rotation. */ + public void markAllForRotation() { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + for (String packageName : mSharedPreferences.getAll().keySet()) { + editor.putInt(packageName, MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION); + } + editor.apply(); + } + + private int getBackupsSinceRotation(String packageName) { + return mSharedPreferences.getInt(packageName, 0); + } +} diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index 01716a0ea40a..11a2fc9c1e45 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -16,23 +16,33 @@ package com.android.server; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + import android.app.AppGlobals; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; +import com.android.internal.os.BinderInternal; +import com.android.internal.os.BinderInternal.CallSession; import com.android.internal.os.CachedDeviceState; import java.io.FileDescriptor; @@ -49,6 +59,106 @@ public class BinderCallsStatsService extends Binder { private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; + /** Resolves the work source of an incoming binder transaction. */ + static class WorkSourceProvider { + private ArraySet<Integer> mAppIdWhitelist; + + WorkSourceProvider() { + mAppIdWhitelist = new ArraySet<>(); + } + + public int resolveWorkSourceUid() { + final int callingUid = getCallingUid(); + final int appId = UserHandle.getAppId(callingUid); + if (mAppIdWhitelist.contains(appId)) { + final int workSource = getCallingWorkSourceUid(); + final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE; + return isWorkSourceSet ? workSource : callingUid; + } + return callingUid; + } + + public void systemReady(Context context) { + mAppIdWhitelist = createAppidWhitelist(context); + } + + public void dump(PrintWriter pw, Map<Integer, String> appIdToPackageName) { + pw.println("AppIds of apps that can set the work source:"); + final ArraySet<Integer> whitelist = mAppIdWhitelist; + for (Integer appId : whitelist) { + pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(appId))); + } + } + + protected int getCallingUid() { + return Binder.getCallingUid(); + } + + protected int getCallingWorkSourceUid() { + return Binder.getCallingWorkSourceUid(); + } + + private ArraySet<Integer> createAppidWhitelist(Context context) { + // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access. + final ArraySet<Integer> whitelist = new ArraySet<>(); + + // We trust our own process. + whitelist.add(Process.myUid()); + // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission. + final PackageManager pm = context.getPackageManager(); + final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS }; + final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; + final List<PackageInfo> packages = + pm.getPackagesHoldingPermissions(permissions, queryFlags); + final int packagesSize = packages.size(); + for (int i = 0; i < packagesSize; i++) { + final PackageInfo pkgInfo = packages.get(i); + try { + final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags); + final int appId = UserHandle.getAppId(uid); + whitelist.add(appId); + } catch (NameNotFoundException e) { + Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e); + } + } + return whitelist; + } + } + + /** Observer for all system server incoming binder transactions. */ + @VisibleForTesting + static class BinderCallsObserver implements BinderInternal.Observer { + private final BinderInternal.Observer mBinderCallsStats; + private final WorkSourceProvider mWorkSourceProvider; + + BinderCallsObserver(BinderInternal.Observer callsStats, + WorkSourceProvider workSourceProvider) { + mBinderCallsStats = callsStats; + mWorkSourceProvider = workSourceProvider; + } + + @Override + public CallSession callStarted(Binder binder, int code) { + // We depend on the code in Binder#execTransact to reset the state of + // ThreadLocalWorkSource + setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid()); + return mBinderCallsStats.callStarted(binder, code); + } + + @Override + public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { + mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize); + } + + @Override + public void callThrewException(CallSession s, Exception exception) { + mBinderCallsStats.callThrewException(s, exception); + } + + protected void setThreadLocalWorkSourceUid(int uid) { + ThreadLocalWorkSource.setUid(uid); + } + } /** Listens for flag changes. */ private static class SettingsObserver extends ContentObserver { @@ -63,13 +173,16 @@ public class BinderCallsStatsService extends Binder { private final Context mContext; private final KeyValueListParser mParser = new KeyValueListParser(','); private final BinderCallsStats mBinderCallsStats; + private final BinderCallsObserver mBinderCallsObserver; - public SettingsObserver(Context context, BinderCallsStats binderCallsStats) { + SettingsObserver(Context context, BinderCallsStats binderCallsStats, + BinderCallsObserver observer) { super(BackgroundThread.getHandler()); mContext = context; context.getContentResolver().registerContentObserver(mUri, false, this, UserHandle.USER_SYSTEM); mBinderCallsStats = binderCallsStats; + mBinderCallsObserver = observer; // Always kick once to ensure that we match current state onChange(); } @@ -107,7 +220,7 @@ public class BinderCallsStatsService extends Binder { mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT); if (mEnabled != enabled) { if (enabled) { - Binder.setObserver(mBinderCallsStats); + Binder.setObserver(mBinderCallsObserver); Binder.setProxyTransactListener( new Binder.PropagateWorkSourceTransactListener()); } else { @@ -116,6 +229,7 @@ public class BinderCallsStatsService extends Binder { } mEnabled = enabled; mBinderCallsStats.reset(); + mBinderCallsStats.setAddDebugEntries(enabled); } } } @@ -154,6 +268,7 @@ public class BinderCallsStatsService extends Binder { public static class LifeCycle extends SystemService { private BinderCallsStatsService mService; private BinderCallsStats mBinderCallsStats; + private WorkSourceProvider mWorkSourceProvider; public LifeCycle(Context context) { super(context); @@ -162,7 +277,11 @@ public class BinderCallsStatsService extends Binder { @Override public void onStart() { mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector()); - mService = new BinderCallsStatsService(mBinderCallsStats); + mWorkSourceProvider = new WorkSourceProvider(); + BinderCallsObserver binderCallsObserver = + new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider); + mService = new BinderCallsStatsService( + mBinderCallsStats, binderCallsObserver, mWorkSourceProvider); publishLocalService(Internal.class, new Internal(mBinderCallsStats)); publishBinderService("binder_calls_stats", mService); boolean detailedTrackingEnabled = SystemProperties.getBoolean( @@ -181,21 +300,29 @@ public class BinderCallsStatsService extends Binder { if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) { CachedDeviceState.Readonly deviceState = getLocalService( CachedDeviceState.Readonly.class); - mService.systemReady(getContext()); mBinderCallsStats.setDeviceState(deviceState); + // It needs to be called before mService.systemReady to make sure the observer is + // initialized before installing it. + mWorkSourceProvider.systemReady(getContext()); + mService.systemReady(getContext()); } } } private SettingsObserver mSettingsObserver; private final BinderCallsStats mBinderCallsStats; + private final BinderCallsObserver mBinderCallsObserver; + private final WorkSourceProvider mWorkSourceProvider; - BinderCallsStatsService(BinderCallsStats binderCallsStats) { + BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer, + WorkSourceProvider workSourceProvider) { mBinderCallsStats = binderCallsStats; + mBinderCallsObserver = observer; + mWorkSourceProvider = workSourceProvider; } public void systemReady(Context context) { - mSettingsObserver = new SettingsObserver(context, mBinderCallsStats); + mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver); } public void reset() { @@ -215,7 +342,7 @@ public class BinderCallsStatsService extends Binder { pw.println("binder_calls_stats reset."); return; } else if ("--enable".equals(arg)) { - Binder.setObserver(mBinderCallsStats); + Binder.setObserver(mBinderCallsObserver); return; } else if ("--disable".equals(arg)) { Binder.setObserver(null); @@ -233,6 +360,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setDetailedTracking(false); pw.println("Detailed tracking disabled"); return; + } else if ("--dump-worksource-provider".equals(arg)) { + mWorkSourceProvider.dump(pw, getAppIdToPackagesMap()); + return; } else if ("-h".equals(arg)) { pw.println("binder_calls_stats commands:"); pw.println(" --reset: Reset stats"); @@ -271,5 +401,4 @@ public class BinderCallsStatsService extends Binder { } return map; } - } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 7ee3d3b3bdc7..126bf6556538 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1236,7 +1236,8 @@ public class IpSecService extends IIpSecService.Stub { OsConstants.UDP_ENCAP, OsConstants.UDP_ENCAP_ESPINUDP); - mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid); + mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner( + new ParcelFileDescriptor(sockFd), callingUid); if (port != 0) { Log.v(TAG, "Binding to port " + port); Os.bind(sockFd, INADDR_ANY, port); @@ -1696,7 +1697,7 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecApplyTransportModeTransform( - socket.getFileDescriptor(), + socket, callingUid, direction, c.getSourceAddress(), @@ -1715,7 +1716,7 @@ public class IpSecService extends IIpSecService.Stub { throws RemoteException { mSrvConfig .getNetdInstance() - .ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); + .ipSecRemoveTransportModeTransform(socket); } /** diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index c563ad224da6..fa3babad639d 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -94,6 +94,8 @@ public class LooperStatsService extends Binder { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; pw.print("Start time: "); pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis())); + pw.print("On battery time (ms): "); + pw.println(mStats.getBatteryTimeMillis()); final List<LooperStats.ExportedEntry> entries = mStats.getEntries(); entries.sort(Comparator .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid) diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java index ccfac80d22a7..bb39ccc52af2 100644 --- a/services/core/java/com/android/server/RuntimeService.java +++ b/services/core/java/com/android/server/RuntimeService.java @@ -94,7 +94,7 @@ public class RuntimeService extends Binder { // Add /data tz data set using the DistroVersion class (which libcore cannot use). // This update mechanism will be removed after the time zone APEX is launched so this // untidiness will disappear with it. - String debugKeyPrefix = "core_library.timezone.data_"; + String debugKeyPrefix = "core_library.timezone.source.data_"; String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile( TimeZoneDistro.DISTRO_VERSION_FILE_NAME); addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 390126c63ab8..dbea529b72fe 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED; import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT; import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT; @@ -2758,6 +2760,14 @@ class StorageManagerService extends IStorageManager.Stub public @Nullable ParcelFileDescriptor openProxyFileDescriptor( int mountId, int fileId, int mode) { Slog.v(TAG, "mountProxyFileDescriptor"); + + // We only support a narrow set of incoming mode flags + if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) { + mode = MODE_READ_WRITE; + } else { + mode = MODE_READ_ONLY; + } + try { synchronized (mAppFuseLock) { if (mAppFuseBridge == null) { diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java index 8cd9d1881639..15de3def913a 100644 --- a/services/core/java/com/android/server/am/ActiveInstrumentation.java +++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java @@ -133,7 +133,7 @@ class ActiveInstrumentation { proto.write(ActiveInstrumentationProto.TARGET_PROCESSES, p); } if (mTargetInfo != null) { - mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO); + mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO, 0); } proto.write(ActiveInstrumentationProto.PROFILE_FILE, mProfileFile); proto.write(ActiveInstrumentationProto.WATCHER, mWatcher.toString()); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a19e9287aa6c..23287cf399ca 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1801,16 +1801,25 @@ public final class ActiveServices { for (int i = clist.size() - 1; i >= 0; i--) { final ConnectionRecord crec = clist.get(i); final ServiceRecord srec = crec.binding.service; - if (srec != null && srec.app != null - && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { - if (group > 0) { - srec.app.connectionService = srec; - srec.app.connectionGroup = group; - srec.app.connectionImportance = importance; + if (srec != null && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + if (srec.app != null) { + if (group > 0) { + srec.app.connectionService = srec; + srec.app.connectionGroup = group; + srec.app.connectionImportance = importance; + } else { + srec.app.connectionService = null; + srec.app.connectionGroup = 0; + srec.app.connectionImportance = 0; + } } else { - srec.app.connectionService = null; - srec.app.connectionGroup = 0; - srec.app.connectionImportance = 0; + if (group > 0) { + srec.pendingConnectionGroup = group; + srec.pendingConnectionImportance = importance; + } else { + srec.pendingConnectionGroup = 0; + srec.pendingConnectionImportance = 0; + } } } } @@ -2058,8 +2067,8 @@ public final class ActiveServices { sInfo.applicationInfo.uid, name.getPackageName(), name.getClassName()); } - r = new ServiceRecord(mAm, ss, className, name, filter, sInfo, callingFromFg, - res); + r = new ServiceRecord(mAm, ss, className, name, filter, sInfo, + callingFromFg, res); res.setService(r); smap.mServicesByInstanceName.put(name, r); smap.mServicesByIntent.put(filter, r); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8842f4198206..c16f1db5c579 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9209,6 +9209,19 @@ public class ActivityManagerService extends IActivityManager.Stub } sdumper.dumpWithClient(); } + if (dumpPackage == null) { + // Intentionally dropping the lock for this, because dumpBinderProxies() will make many + // outgoing binder calls to retrieve interface descriptors; while that is system code, + // there is nothing preventing an app from overriding this implementation by talking to + // the binder driver directly, and hang up system_server in the process. So, dump + // without locks held, and even then only when there is an unreasonably large number of + // proxies in the first place. + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */); + } synchronized(this) { pw.println(); if (dumpAll) { @@ -9274,19 +9287,6 @@ public class ActivityManagerService extends IActivityManager.Stub } dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); } - if (dumpPackage == null) { - // Intentionally dropping the lock for this, because dumpBinderProxies() will make many - // outgoing binder calls to retrieve interface descriptors; while that is system code, - // there is nothing preventing an app from overriding this implementation by talking to - // the binder driver directly, and hang up system_server in the process. So, dump - // without locks held, and even then only when there is an unreasonably large number of - // proxies in the first place. - pw.println(); - if (dumpAll) { - pw.println("-------------------------------------------------------------------------------"); - } - dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */); - } } /** @@ -10266,7 +10266,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { continue; } - r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS); + r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS, mProcessList.mLruProcesses.indexOf(r) + ); if (r.isPersistent()) { numPers++; } @@ -10278,7 +10279,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { continue; } - r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS); + r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS, + mProcessList.mLruProcesses.indexOf(r) + ); } for (int i=0; i<mActiveInstrumentation.size(); i++) { @@ -10376,7 +10379,7 @@ public class ActivityManagerService extends IActivityManager.Stub writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage); mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage); - mAtmInternal.writeProcessesToProto(proto, dumpPackage); + mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode); if (dumpPackage == null) { mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER); @@ -10406,14 +10409,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - if (dumpPackage == null) { - final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS); - proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS, - PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness)); - proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode); - proto.end(sleepToken); - } - if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { if (dumpPackage == null || dumpPackage.equals(mDebugApp) diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 62f100926581..8cf9f1e9ae4a 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -537,40 +537,44 @@ public final class ProcessList { } private static String buildOomTag(String prefix, String space, int val, int base) { - if (val == base) { + final int diff = val - base; + if (diff == 0) { if (space == null) return prefix; - return prefix + " "; + return prefix + space; } - return prefix + "+" + Integer.toString(val - base); + if (diff < 10) { + return prefix + "+ " + Integer.toString(diff); + } + return prefix + "+" + Integer.toString(diff); } public static String makeOomAdjString(int setAdj) { if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { - return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ); + return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ); } else if (setAdj >= ProcessList.SERVICE_B_ADJ) { - return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ); + return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ); } else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) { - return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ); + return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ); } else if (setAdj >= ProcessList.HOME_APP_ADJ) { - return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ); + return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ); } else if (setAdj >= ProcessList.SERVICE_ADJ) { - return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ); + return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ); } else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); + return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); } else if (setAdj >= ProcessList.BACKUP_APP_ADJ) { - return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ); + return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ); } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { - return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); + return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); } else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) { - return buildOomTag("vis ", null, setAdj, ProcessList.VISIBLE_APP_ADJ); + return buildOomTag("vis", " ", setAdj, ProcessList.VISIBLE_APP_ADJ); } else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) { - return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ); + return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ); } else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) { - return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ); + return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ); } else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) { - return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ); + return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ); } else if (setAdj >= ProcessList.SYSTEM_ADJ) { - return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ); + return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ); } else if (setAdj >= ProcessList.NATIVE_ADJ) { return buildOomTag("ntv ", null, setAdj, ProcessList.NATIVE_ADJ); } else { @@ -2237,6 +2241,191 @@ public final class ProcessList { return index; } + /** + * Handle the case where we are inserting a process hosting client activities: + * Make sure any groups have their order match their importance, and take care of + * distributing old clients across other activity processes so they can't spam + * the LRU list. Processing of the list will be restricted by the indices provided, + * and not extend out of them. + * + * @param topApp The app at the top that has just been inserted in to the list. + * @param topI The position in the list where topApp was inserted; this is the start (at the + * top) where we are going to do our processing. + * @param bottomI The last position at which we will be processing; this is the end position + * of whichever section of the LRU list we are in. Nothing past it will be + * touched. + * @param endIndex The current end of the top being processed. Typically topI - 1. That is, + * where we are going to start potentially adjusting other entries in the list. + */ + private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI, + final int bottomI, int endIndex) { + if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity + || !topApp.hasClientActivities()) { + // If this is not a special process that has client activities, then there is + // nothing to do. + return; + } + + final int uid = topApp.info.uid; + if (topApp.connectionGroup > 0) { + int endImportance = topApp.connectionImportance; + for (int i = endIndex; i >= bottomI; i--) { + final ProcessRecord subProc = mLruProcesses.get(i); + if (subProc.info.uid == uid + && subProc.connectionGroup == topApp.connectionGroup) { + if (i == endIndex && subProc.connectionImportance >= endImportance) { + // This process is already in the group, and its importance + // is not as strong as the process before it, so keep it + // correctly positioned in the group. + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Keeping in-place above " + subProc + + " endImportance=" + endImportance + + " group=" + subProc.connectionGroup + + " importance=" + subProc.connectionImportance); + endIndex--; + endImportance = subProc.connectionImportance; + } else { + // We want to pull this up to be with the rest of the group, + // and order within the group by importance. + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Pulling up " + subProc + + " to position in group with importance=" + + subProc.connectionImportance); + boolean moved = false; + for (int pos = topI; pos > endIndex; pos--) { + final ProcessRecord posProc = mLruProcesses.get(pos); + if (subProc.connectionImportance + <= posProc.connectionImportance) { + mLruProcesses.remove(i); + mLruProcesses.add(pos, subProc); + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Moving " + subProc + + " from position " + i + " to above " + posProc + + " @ " + pos); + moved = true; + endIndex--; + break; + } + } + if (!moved) { + // Goes to the end of the group. + mLruProcesses.remove(i); + mLruProcesses.add(endIndex - 1, subProc); + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Moving " + subProc + + " from position " + i + " to end of group @ " + + endIndex); + endIndex--; + endImportance = subProc.connectionImportance; + } + } + } + } + + } + // To keep it from spamming the LRU list (by making a bunch of clients), + // we will distribute other entries owned by it to be in-between other apps. + int i = endIndex; + while (i >= bottomI) { + ProcessRecord subProc = mLruProcesses.get(i); + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Looking to spread old procs, at " + subProc + " @ " + i); + if (subProc.info.uid != uid) { + // This is a different app... if we have gone through some of the + // target app, pull this up to be before them. We want to pull up + // one activity process, but any number of non-activity processes. + if (i < endIndex) { + boolean hasActivity = false; + int connUid = 0; + int connGroup = 0; + while (i >= bottomI) { + mLruProcesses.remove(i); + mLruProcesses.add(endIndex, subProc); + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Different app, moving to " + endIndex); + i--; + if (i < bottomI) { + break; + } + subProc = mLruProcesses.get(i); + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Looking at next app at " + i + ": " + subProc); + if (subProc.hasActivitiesOrRecentTasks() || subProc.treatLikeActivity) { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "This is hosting an activity!"); + if (hasActivity) { + // Already found an activity, done. + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Already found an activity, done"); + break; + } + hasActivity = true; + } else if (subProc.hasClientActivities()) { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "This is a client of an activity"); + if (hasActivity) { + if (connUid == 0 || connUid != subProc.info.uid) { + // Already have an activity that is not from from a client + // connection or is a different client connection, done. + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Already found a different activity: connUid=" + + connUid + " uid=" + subProc.info.uid); + break; + } else if (connGroup == 0 || connGroup != subProc.connectionGroup) { + // Previously saw a different group or not from a group, + // want to treat these as different things. + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Already found a different group: connGroup=" + + connGroup + " group=" + subProc.connectionGroup); + break; + } + } else { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "This is an activity client! uid=" + + subProc.info.uid + " group=" + subProc.connectionGroup); + hasActivity = true; + connUid = subProc.info.uid; + connGroup = subProc.connectionGroup; + } + } + endIndex--; + } + } + // Find the end of the next group of processes for target app. This + // is after any entries of different apps (so we don't change the existing + // relative order of apps) and then after the next last group of processes + // of the target app. + for (endIndex--; endIndex >= bottomI; endIndex--) { + final ProcessRecord endProc = mLruProcesses.get(endIndex); + if (endProc.info.uid == uid) { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Found next group of app: " + endProc + " @ " + + endIndex); + break; + } + } + if (endIndex >= bottomI) { + final ProcessRecord endProc = mLruProcesses.get(endIndex); + for (endIndex--; endIndex >= bottomI; endIndex--) { + final ProcessRecord nextEndProc = mLruProcesses.get(endIndex); + if (nextEndProc.info.uid != uid + || nextEndProc.connectionGroup != endProc.connectionGroup) { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Found next group or app: " + nextEndProc + " @ " + + endIndex + " group=" + nextEndProc.connectionGroup); + break; + } + } + } + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Bumping scan position to " + endIndex); + i = endIndex; + } else { + i--; + } + } + } + final void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) { final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities() @@ -2349,91 +2538,31 @@ public final class ProcessList { if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity && mLruProcessActivityStart < (N - 1)) { // Process doesn't have activities, but has clients with - // activities... move it up, but one below the top (the top - // should always have a real activity). + // activities... move it up, but below the app that is binding to it. if (DEBUG_LRU) Slog.d(TAG_LRU, - "Adding to second-top of LRU activity list: " + app); - mLruProcesses.add(N - 1, app); - // If this process is part of a group, need to pull up any other processes - // in that group to be with it. - final int uid = app.info.uid; - int endIndex = N - 2; - nextActivityIndex = N - 2; - if (app.connectionGroup > 0) { - int endImportance = app.connectionImportance; - for (int i = endIndex; i >= mLruProcessActivityStart; i--) { - final ProcessRecord subProc = mLruProcesses.get(i); - if (subProc.info.uid == uid - && subProc.connectionGroup == subProc.connectionGroup) { - if (i == endIndex && subProc.connectionImportance >= endImportance) { - // This process is already in the group, and its importance - // is not as strong as the process before it, so it keep it - // correctly positioned in the group. - endIndex--; - endImportance = subProc.connectionImportance; - } else { - // We want to pull this up to be with the rest of the group, - // and order within the group by importance. - boolean moved = false; - for (int pos = N - 1; pos > endIndex; pos--) { - final ProcessRecord posProc = mLruProcesses.get(pos); - if (subProc.connectionImportance - <= posProc.connectionImportance) { - mLruProcesses.remove(i); - mLruProcesses.add(pos, subProc); - moved = true; - endIndex--; - break; - } - } - if (!moved) { - // Goes to the end of the group. - mLruProcesses.remove(i); - mLruProcesses.add(endIndex - 1, subProc); - endIndex--; - endImportance = subProc.connectionImportance; - } - } - } + "Adding to second-top of LRU activity list: " + app + + " group=" + app.connectionGroup + + " importance=" + app.connectionImportance); + int pos = N - 1; + while (pos > mLruProcessActivityStart) { + final ProcessRecord posproc = mLruProcesses.get(pos); + if (posproc.info.uid == app.info.uid) { + // Technically this app could have multiple processes with different + // activities and so we should be looking for the actual process that + // is bound to the target proc... but I don't really care, do you? + break; } - + pos--; } - // To keep it from spamming the LRU list (by making a bunch of clients), - // we will distribute other entries owned by it to be in-between other apps. - for (int i = endIndex; i >= mLruProcessActivityStart; i--) { - final ProcessRecord subProc = mLruProcesses.get(i); - if (subProc.info.uid != uid) { - // This is a different app... if we have gone through some of the - // target app, pull this up to be before them. - if (i < endIndex) { - mLruProcesses.remove(i); - mLruProcesses.add(endIndex, subProc); - } - // Find the end of the next group of processes for target app. This - // is after any entries of different apps (so we don't change the existing - // relative order of apps) and then after the next last group of processes - // of the target app. - for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) { - final ProcessRecord endProc = mLruProcesses.get(endIndex); - if (endProc.info.uid == uid) { - break; - } - } - if (endIndex >= mLruProcessActivityStart) { - final ProcessRecord endProc = mLruProcesses.get(endIndex); - for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) { - final ProcessRecord nextEndProc = mLruProcesses.get(endIndex); - if (nextEndProc.info.uid != uid - || nextEndProc.connectionGroup != endProc.connectionGroup) { - break; - } - } - } - if (i > endIndex) { - i = endIndex; - } - } + mLruProcesses.add(pos, app); + // If this process is part of a group, need to pull up any other processes + // in that group to be with it. + int endIndex = pos - 1; + if (endIndex < mLruProcessActivityStart) { + endIndex = mLruProcessActivityStart; } + nextActivityIndex = endIndex; + updateClientActivitiesOrdering(app, pos, mLruProcessActivityStart, endIndex); } else { // Process has activities, put it at the very tipsy-top. if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app); @@ -2469,6 +2598,9 @@ public final class ProcessList { nextIndex = index - 1; mLruProcessActivityStart++; mLruProcessServiceStart++; + if (index > 1) { + updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1); + } } app.lruSeq = mLruSeq; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 013de93c7f24..4826f486da89 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -586,7 +586,7 @@ final class ProcessRecord implements WindowProcessListener { } origBase.makeInactive(); } - baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid, + baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, info.longVersionCode, processName); baseProcessTracker.makeActive(); for (int i=0; i<pkgList.size(); i++) { @@ -594,7 +594,7 @@ final class ProcessRecord implements WindowProcessListener { if (holder.state != null && holder.state != origBase) { holder.state.makeInactive(); } - tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), uid, + tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, info.longVersionCode, processName); if (holder.state != baseProcessTracker) { holder.state.makeActive(); @@ -760,19 +760,25 @@ final class ProcessRecord implements WindowProcessListener { @Override public void writeToProto(ProtoOutputStream proto, long fieldId) { + writeToProto(proto, fieldId, -1); + } + + public void writeToProto(ProtoOutputStream proto, long fieldId, int lruIndex) { long token = proto.start(fieldId); proto.write(ProcessRecordProto.PID, pid); proto.write(ProcessRecordProto.PROCESS_NAME, processName); - if (info.uid < Process.FIRST_APPLICATION_UID) { - proto.write(ProcessRecordProto.UID, uid); - } else { + proto.write(ProcessRecordProto.UID, info.uid); + if (UserHandle.getAppId(info.uid) >= Process.FIRST_APPLICATION_UID) { proto.write(ProcessRecordProto.USER_ID, userId); proto.write(ProcessRecordProto.APP_ID, UserHandle.getAppId(info.uid)); - if (uid != info.uid) { - proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid)); - } + } + if (uid != info.uid) { + proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid)); } proto.write(ProcessRecordProto.PERSISTENT, mPersistent); + if (lruIndex >= 0) { + proto.write(ProcessRecordProto.LRU_INDEX, lruIndex); + } proto.end(token); } @@ -864,7 +870,8 @@ final class ProcessRecord implements WindowProcessListener { ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( versionCode); if (baseProcessTracker != null) { - tracker.updateProcessStateHolderLocked(holder, pkg, uid, versionCode, processName); + tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, + processName); pkgList.put(pkg, holder); if (holder.state != baseProcessTracker) { holder.state.makeActive(); @@ -925,7 +932,7 @@ final class ProcessRecord implements WindowProcessListener { pkgList.clear(); ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( info.longVersionCode); - tracker.updateProcessStateHolderLocked(holder, info.packageName, uid, + tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, info.longVersionCode, processName); pkgList.put(info.packageName, holder); if (holder.state != baseProcessTracker) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 09f8c3eee3b6..da5ce1c45610 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -121,6 +121,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN long nextRestartTime; // time when restartDelay will expire. boolean destroying; // set when we have started destroying the service long destroyTime; // time at which destory was initiated. + int pendingConnectionGroup; // To be filled in to ProcessRecord once it connects + int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects String stringName; // caching of toString @@ -386,6 +388,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(" restartTime="); TimeUtils.formatDuration(restartTime, now, pw); pw.print(" createdFromFg="); pw.println(createdFromFg); + if (pendingConnectionGroup != 0) { + pw.print(prefix); pw.print(" pendingConnectionGroup="); + pw.print(pendingConnectionGroup); + pw.print(" Importance="); pw.println(pendingConnectionImportance); + } if (startRequested || delayedStop || lastStartId != 0) { pw.print(prefix); pw.print("startRequested="); pw.print(startRequested); pw.print(" delayedStop="); pw.print(delayedStop); @@ -461,7 +468,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN serviceInfo = sInfo; appInfo = sInfo.applicationInfo; packageName = sInfo.applicationInfo.packageName; - processName = sInfo.processName; + if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + processName = sInfo.processName + ":" + instanceName.getClassName(); + } else { + processName = sInfo.processName; + } permission = sInfo.permission; exported = sInfo.exported; this.restarter = restarter; @@ -507,6 +518,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void setProcess(ProcessRecord _proc) { app = _proc; + if (pendingConnectionGroup > 0) { + app.connectionService = this; + app.connectionGroup = pendingConnectionGroup; + app.connectionImportance = pendingConnectionImportance; + pendingConnectionGroup = pendingConnectionImportance = 0; + } if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) { for (int conni = connections.size() - 1; conni >= 0; conni--) { ArrayList<ConnectionRecord> cr = connections.valueAt(conni); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 353f787f9005..271b37ec7568 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1144,7 +1144,7 @@ class UserController implements Handler.Callback { /** * Attempt to unlock user without a credential token. This typically * succeeds when the device doesn't have credential-encrypted storage, or - * when the the credential-encrypted storage isn't tied to a user-provided + * when the credential-encrypted storage isn't tied to a user-provided * PIN or pattern. */ private boolean maybeUnlockUser(final int userId) { diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 0b6786cf8c07..521fa236cf7d 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TypeEvaluator; @@ -29,8 +31,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.hardware.display.IColorDisplayManager; import android.net.Uri; import android.opengl.Matrix; +import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -39,6 +43,7 @@ import android.util.MathUtils; import android.util.Slog; import android.view.animation.AnimationUtils; +import com.android.internal.R; import com.android.internal.app.ColorDisplayController; import com.android.server.SystemService; import com.android.server.twilight.TwilightListener; @@ -49,12 +54,8 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; -import com.android.internal.R; - -import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; - /** - * Tints the display at night. + * Controls the display's color transforms. */ public final class ColorDisplayService extends SystemService implements ColorDisplayController.Callback { @@ -101,7 +102,7 @@ public final class ColorDisplayService extends SystemService @Override public void onStart() { - // Nothing to publish. + publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService()); } @Override @@ -171,7 +172,7 @@ public final class ColorDisplayService extends SystemService } }; cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE), - false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser); + false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser); } else if (mBootCompleted) { setUp(); } @@ -405,8 +406,8 @@ public final class ColorDisplayService extends SystemService } /** - * Returns the first date time corresponding to the local time that occurs before the - * provided date time. + * Returns the first date time corresponding to the local time that occurs before the provided + * date time. * * @param compareTime the LocalDateTime to compare against * @return the prior LocalDateTime corresponding to this local time @@ -420,8 +421,8 @@ public final class ColorDisplayService extends SystemService } /** - * Returns the first date time corresponding to this local time that occurs after the - * provided date time. + * Returns the first date time corresponding to this local time that occurs after the provided + * date time. * * @param compareTime the LocalDateTime to compare against * @return the next LocalDateTime corresponding to this local time @@ -434,6 +435,11 @@ public final class ColorDisplayService extends SystemService return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; } + private boolean isDeviceColorManagedInternal() { + final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); + return dtm.isDeviceColorManaged(); + } + private abstract class AutoMode implements ColorDisplayController.Callback { public abstract void onStart(); @@ -616,4 +622,16 @@ public final class ColorDisplayService extends SystemService return mResultMatrix; } } + + private final class BinderService extends IColorDisplayManager.Stub { + @Override + public boolean isDeviceColorManaged() { + final long token = Binder.clearCallingIdentity(); + try { + return isDeviceColorManagedInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } } diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java index d6931e006479..5ca1755131ab 100644 --- a/services/core/java/com/android/server/display/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/DisplayTransformManager.java @@ -16,7 +16,6 @@ package com.android.server.display; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.opengl.Matrix; import android.os.IBinder; @@ -27,8 +26,10 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Slog; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.app.ColorDisplayController; + import java.util.Arrays; /** @@ -59,10 +60,6 @@ public class DisplayTransformManager { private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015; private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014; - - private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; - private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode"; - /** * SurfaceFlinger global saturation factor. */ @@ -71,6 +68,10 @@ public class DisplayTransformManager { * SurfaceFlinger display color (managed, unmanaged, etc.). */ private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023; + private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1030; + + private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; + private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode"; private static final float COLOR_SATURATION_NATURAL = 1.0f; private static final float COLOR_SATURATION_BOOSTED = 1.1f; @@ -269,6 +270,29 @@ public class DisplayTransformManager { } /** + * Returns whether the screen is wide color gamut via SurfaceFlinger's + * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}. + */ + public boolean isDeviceColorManaged() { + final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); + if (flinger != null) { + final Parcel data = Parcel.obtain(); + final Parcel reply = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + try { + flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0); + return reply.readBoolean(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to query wide color support", ex); + } finally { + data.recycle(); + reply.recycle(); + } + } + return false; + } + + /** * Propagates the provided saturation to the SurfaceFlinger. */ private void applySaturation(float saturation) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index b148a2f3fff5..c0d3fdfb8f91 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -980,7 +980,7 @@ public class HdmiControlService extends SystemService { * @param sourceAddress a logical address of source device where sends polling message * @param pickStrategy strategy how to pick polling candidates * @param retryCount the number of retry used to send polling message to remote devices - * @throw IllegalArgumentException if {@code pickStrategy} is invalid value + * @throws IllegalArgumentException if {@code pickStrategy} is invalid value */ @ServiceThreadOnly void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 1759ce195485..96095d89687e 100644 --- a/services/core/java/com/android/server/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server; +package com.android.server.infra; import android.annotation.NonNull; import android.annotation.Nullable; @@ -39,6 +39,9 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.Preconditions; +import com.android.server.LocalServices; +import com.android.server.SystemService; import java.io.PrintWriter; import java.util.List; @@ -211,6 +214,66 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } /** + * Temporary sets the service implementation. + * + * <p>Typically used by Shell command and/or CTS tests. + * + * @param componentName name of the new component + * @param durationMs how long the change will be valid (the service will be automatically reset + * to the default component after this timeout expires). + * @throws SecurityException if caller is not allowed to manage this service's settings. + * @throws IllegalArgumentException if value of {@code durationMs} is higher than + * {@link #getMaximumTemporaryServiceDurationMs()}. + */ + public final void setTemporaryService(@UserIdInt int userId, @NonNull String componentName, + int durationMs) { + Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for " + + durationMs + "ms"); + enforceCallingPermissionForManagement(); + + Preconditions.checkNotNull(componentName); + final int maxDurationMs = getMaximumTemporaryServiceDurationMs(); + if (durationMs > maxDurationMs) { + throw new IllegalArgumentException( + "Max duration is " + maxDurationMs + " (called with " + durationMs + ")"); + } + + synchronized (mLock) { + final S service = getServiceForUserLocked(userId); + if (service != null) { + service.setTemporaryServiceLocked(componentName, durationMs); + } + } + } + + /** + * Gets the maximum time the service implementation can be changed. + * + * @throws UnsupportedOperationException if subclass doesn't override it. + */ + protected int getMaximumTemporaryServiceDurationMs() { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + + /** + * Resets the temporary service implementation to the default component. + * + * <p>Typically used by Shell command and/or CTS tests. + * + * @throws SecurityException if caller is not allowed to manage this service's settings. + */ + public final void resetTemporaryService(@UserIdInt int userId) { + Slog.i(mTag, "resetTemporaryService(): " + userId); + enforceCallingPermissionForManagement(); + synchronized (mLock) { + final S service = getServiceForUserLocked(userId); + if (service != null) { + service.resetTemporaryServiceLocked(); + } + } + } + + /** * Asserts that the caller has permissions to manage this service. * * <p>Typically called by {@code ShellCommand} implementations. diff --git a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java index f532b22cb8d4..513a6a3eeec8 100644 --- a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java +++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.infra; import android.annotation.NonNull; import android.content.ComponentName; diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java index 001d85f1b798..dfe8a369db44 100644 --- a/services/core/java/com/android/server/AbstractPerUserSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server; +package com.android.server.infra; import android.annotation.CallSuper; import android.annotation.NonNull; @@ -26,12 +26,17 @@ import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.Process; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; @@ -49,6 +54,9 @@ import java.io.PrintWriter; public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>, M extends AbstractMasterSystemService<M, S>> { + /** Handler message to {@link #resetTemporaryServiceLocked()} */ + private static final int MSG_RESET_TEMPORARY_SERVICE = 0; + protected final @UserIdInt int mUserId; protected final Object mLock; protected final String mTag = getClass().getSimpleName(); @@ -70,6 +78,26 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst @GuardedBy("mLock") private ServiceInfo mServiceInfo; + /** + * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}. + * + * <p>Typically used by Shell command and/or CTS tests. + */ + @GuardedBy("mLock") + private String mTemporaryServiceName; + + /** + * When the temporary service will expire (and reset back to the default). + */ + @GuardedBy("mLock") + private long mTemporaryServiceExpiration; + + /** + * Handler used to reset the temporary service name. + */ + @GuardedBy("mLock") + private Handler mTemporaryHandler; + protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock, @UserIdInt int userId) { mMaster = master; @@ -130,7 +158,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst mDisabled = disabled; ComponentName serviceComponent = null; ServiceInfo serviceInfo = null; - final String componentName = getComponentNameFromSettings(); + final String componentName = getComponentNameLocked(); if (!TextUtils.isEmpty(componentName)) { try { serviceComponent = ComponentName.unflattenFromString(componentName); @@ -191,6 +219,29 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** + * Gets the current name of the service, which is either the + * {@link #getDefaultComponentName() default service} or the + * {@link #setTemporaryServiceLocked(String, int) temporary one}. + */ + protected final String getComponentNameLocked() { + if (mTemporaryServiceName != null) { + // Always log it, as it should only be used on CTS or during development + Slog.w(mTag, "getComponentName(): using temporary name " + mTemporaryServiceName); + return mTemporaryServiceName; + } + return getDefaultComponentName(); + } + + /** + * Gets the name of the default component for the service. + * + * <p>Typically implemented by returning {@link #getComponentNameFromSettings()} or by using + * a string from the system resources. + */ + @Nullable + protected abstract String getDefaultComponentName(); + + /** * Gets this name of the remote service this service binds to as defined by {@link Settings}. */ @Nullable @@ -201,6 +252,66 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** + * Checks whether the current service for the user was temporarily set. + */ + public final boolean isTemporaryServiceSetLocked() { + return mTemporaryServiceName != null; + } + + /** + * Temporary sets the service implementation. + * + * @param componentName name of the new component + * @param durationMs how long the change will be valid (the service will be automatically reset + * to the default component after this timeout expires). + */ + protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) { + mTemporaryServiceName = componentName; + + if (mTemporaryHandler == null) { + mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_RESET_TEMPORARY_SERVICE) { + synchronized (mLock) { + resetTemporaryServiceLocked(); + } + } else { + Slog.wtf(mTag, "invalid handler msg: " + msg); + } + } + }; + } else { + removeResetTemporaryServiceMessageLocked(); + } + mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs; + mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs); + + updateLocked(mDisabled); + } + + private void removeResetTemporaryServiceMessageLocked() { + if (mMaster.verbose) { + Slog.v(mTag, "setTemporaryServiceLocked(): removing old message"); + } + // NOTE: caller should already have checked it + mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); + } + + /** + * Resets the temporary service implementation to the default component. + */ + protected final void resetTemporaryServiceLocked() { + Slog.i(mTag, "resetting temporary service from " + mTemporaryServiceName); + mTemporaryServiceName = null; + if (mTemporaryHandler != null) { + removeResetTemporaryServiceMessageLocked(); + mTemporaryHandler = null; + } + updateLocked(mDisabled); + } + + /** * Gets the {@link ComponentName} of the remote service this service binds to, or {@code null} * if the service is disabled. */ @@ -290,12 +401,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst pw.print(prefix); pw.print("Service UID: "); pw.println(mServiceInfo.applicationInfo.uid); } - final String componentName = getComponentNameFromSettings(); - if (componentName != null) { - pw.print(prefix); pw.print("Service name: "); - pw.println(componentName); + if (mTemporaryServiceName != null) { + pw.print(prefix); pw.print("Temporary service name: "); pw.print(mTemporaryServiceName); + final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime(); + pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.println(")"); + pw.print(prefix); pw.print(prefix); + pw.print("Default service name: "); pw.println(getDefaultComponentName()); } else { - pw.println("No service package set"); + pw.print(prefix); pw.print("Service name: "); pw.println(getDefaultComponentName()); } } } diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/infra/AbstractRemoteService.java index f636487c666b..7af1d4ca94ef 100644 --- a/services/core/java/com/android/server/AbstractRemoteService.java +++ b/services/core/java/com/android/server/infra/AbstractRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.infra; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -33,6 +33,7 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.server.FgThread; import java.io.PrintWriter; import java.lang.ref.WeakReference; diff --git a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java index 8e1f540a4d5e..8f8b448a7a66 100644 --- a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java +++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.infra; import android.annotation.NonNull; import android.content.ComponentName; diff --git a/services/core/java/com/android/server/infra/package.html b/services/core/java/com/android/server/infra/package.html new file mode 100644 index 000000000000..61a4c433cdaf --- /dev/null +++ b/services/core/java/com/android/server/infra/package.html @@ -0,0 +1,6 @@ +<html> +<body> +Contains common classes providing the plumbing infrastructure necessary to implement a system +service +</body> +</html>
\ No newline at end of file diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java index d5be26ac3389..f424869a428e 100644 --- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java +++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java @@ -16,10 +16,12 @@ package com.android.server.intelligence; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.os.Bundle; import android.os.IBinder; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; /** @@ -53,6 +55,7 @@ public abstract class IntelligenceManagerInternal { * @param activityToken activity that originated this request. * @param autofillSessionId autofill session id (must be used on {@code client} calls. * @param focusedId id of the the field that triggered this request. + * @param focusedValue current value of the field that triggered this request. * * @return {@code false} if the service cannot handle this request, {@code true} otherwise. * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the @@ -60,7 +63,8 @@ public abstract class IntelligenceManagerInternal { */ public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId, @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken, - int autofillSessionId, @NonNull AutofillId focusedId); + int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue); /** * Callback used by the Autofill Session to communicate with the Augmented Autofill service. diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index b3f0629ea2d3..ea295de5909f 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -78,7 +78,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; @@ -97,6 +96,7 @@ import com.android.server.job.controllers.ContentObserverController; import com.android.server.job.controllers.DeviceIdleJobsController; import com.android.server.job.controllers.IdleController; import com.android.server.job.controllers.JobStatus; +import com.android.server.job.controllers.QuotaController; import com.android.server.job.controllers.StateController; import com.android.server.job.controllers.StorageController; import com.android.server.job.controllers.TimeController; @@ -245,11 +245,11 @@ public class JobSchedulerService extends com.android.server.SystemService * Named indices into the STANDBY_BEATS array, for clarity in referring to * specific buckets' bookkeeping. */ - static final int ACTIVE_INDEX = 0; - static final int WORKING_INDEX = 1; - static final int FREQUENT_INDEX = 2; - static final int RARE_INDEX = 3; - static final int NEVER_INDEX = 4; + public static final int ACTIVE_INDEX = 0; + public static final int WORKING_INDEX = 1; + public static final int FREQUENT_INDEX = 2; + public static final int RARE_INDEX = 3; + public static final int NEVER_INDEX = 4; /** * Bookkeeping about when jobs last run. We keep our own record in heartbeat time, @@ -308,6 +308,10 @@ public class JobSchedulerService extends com.android.server.SystemService try { mConstants.updateConstantsLocked(Settings.Global.getString(mResolver, Settings.Global.JOB_SCHEDULER_CONSTANTS)); + for (int controller = 0; controller < mControllers.size(); controller++) { + final StateController sc = mControllers.get(controller); + sc.onConstantsUpdatedLocked(); + } } catch (IllegalArgumentException e) { // Failed to parse the settings string, log this and move on // with defaults. @@ -315,8 +319,10 @@ public class JobSchedulerService extends com.android.server.SystemService } } - // Reset the heartbeat alarm based on the new heartbeat duration - setNextHeartbeatAlarm(); + if (mConstants.USE_HEARTBEATS) { + // Reset the heartbeat alarm based on the new heartbeat duration + setNextHeartbeatAlarm(); + } } } @@ -352,6 +358,19 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats"; private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; + private static final String KEY_USE_HEARTBEATS = "use_heartbeats"; + private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = + "qc_allowed_time_per_period_ms"; + private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = + "qc_in_quota_buffer_ms"; + private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = + "qc_window_size_active_ms"; + private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = + "qc_window_size_working_ms"; + private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = + "qc_window_size_frequent_ms"; + private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = + "qc_window_size_rare_ms"; private static final int DEFAULT_MIN_IDLE_COUNT = 1; private static final int DEFAULT_MIN_CHARGING_COUNT = 1; @@ -377,6 +396,19 @@ public class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; + private static final boolean DEFAULT_USE_HEARTBEATS = true; + private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = + 10 * 60 * 1000L; // 10 minutes + private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = + 30 * 1000L; // 30 seconds + private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = + 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time + private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = + 2 * 60 * 60 * 1000L; // 2 hours + private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = + 8 * 60 * 60 * 1000L; // 8 hours + private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = + 24 * 60 * 60 * 1000L; // 24 hours /** * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things @@ -495,6 +527,54 @@ public class JobSchedulerService extends com.android.server.SystemService * we consider matching it against a metered network. */ public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; + /** + * Whether to use heartbeats or rolling window for quota management. True will use + * heartbeats, false will use a rolling window. + */ + public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS; + + /** How much time each app will have to run jobs within their standby bucket window. */ + public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = + DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS; + + /** + * How much time the package should have before transitioning from out-of-quota to in-quota. + * This should not affect processing if the package is already in-quota. + */ + public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = + DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -567,6 +647,25 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_CONN_CONGESTION_DELAY_FRAC); CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC, DEFAULT_CONN_PREFETCH_RELAX_FRAC); + USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS); + QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS, + DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS); + QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS, + DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS); + QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS, + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS); + QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS, + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS); + QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS, + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS); + QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis( + KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS, + DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS); } void dump(IndentingPrintWriter pw) { @@ -600,6 +699,19 @@ public class JobSchedulerService extends com.android.server.SystemService pw.println('}'); pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); + pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS, + QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS, + QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println(); + pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println(); pw.decreaseIndent(); } @@ -629,6 +741,23 @@ public class JobSchedulerService extends com.android.server.SystemService } proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC); + proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS); + + final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER); + proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS, + QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS); + proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, + QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS); + proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS); + proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS); + proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS); + proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, + QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS); + proto.end(qcToken); + proto.end(token); } } @@ -1162,6 +1291,7 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(new ContentObserverController(this)); mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); + mControllers.add(new QuotaController(this)); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. @@ -1225,7 +1355,9 @@ public class JobSchedulerService extends com.android.server.SystemService mAppStateTracker = Preconditions.checkNotNull( LocalServices.getService(AppStateTracker.class)); - setNextHeartbeatAlarm(); + if (mConstants.USE_HEARTBEATS) { + setNextHeartbeatAlarm(); + } // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); @@ -1869,6 +2001,9 @@ public class JobSchedulerService extends com.android.server.SystemService // Intentionally does not touch the alarm timing void advanceHeartbeatLocked(long beatsElapsed) { + if (!mConstants.USE_HEARTBEATS) { + return; + } mHeartbeat += beatsElapsed; if (DEBUG_STANDBY) { Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed @@ -1904,6 +2039,9 @@ public class JobSchedulerService extends com.android.server.SystemService void setNextHeartbeatAlarm() { final long heartbeatLength; synchronized (mLock) { + if (!mConstants.USE_HEARTBEATS) { + return; + } heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME; } final long now = sElapsedRealtimeClock.millis(); @@ -1976,48 +2114,51 @@ public class JobSchedulerService extends com.android.server.SystemService return false; } - // If the app is in a non-active standby bucket, make sure we've waited - // an appropriate amount of time since the last invocation. During device- - // wide parole, standby bucketing is ignored. - // - // Jobs in 'active' apps are not subject to standby, nor are jobs that are - // specifically marked as exempt. - if (DEBUG_STANDBY) { - Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() - + " parole=" + mInParole + " active=" + job.uidActive - + " exempt=" + job.getJob().isExemptedFromAppStandby()); - } - if (!mInParole - && !job.uidActive - && !job.getJob().isExemptedFromAppStandby()) { - final int bucket = job.getStandbyBucket(); + if (mConstants.USE_HEARTBEATS) { + // If the app is in a non-active standby bucket, make sure we've waited + // an appropriate amount of time since the last invocation. During device- + // wide parole, standby bucketing is ignored. + // + // Jobs in 'active' apps are not subject to standby, nor are jobs that are + // specifically marked as exempt. if (DEBUG_STANDBY) { - Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat - + " next=" + mNextBucketHeartbeat[bucket]); - } - if (mHeartbeat < mNextBucketHeartbeat[bucket]) { - // Only skip this job if the app is still waiting for the end of its nominal - // bucket interval. Once it's waited that long, we let it go ahead and clear. - // The final (NEVER) bucket is special; we never age those apps' jobs into - // runnability. - final long appLastRan = heartbeatWhenJobsLastRun(job); - if (bucket >= mConstants.STANDBY_BEATS.length - || (mHeartbeat > appLastRan - && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) { - // TODO: log/trace that we're deferring the job due to bucketing if we hit this - if (job.getWhenStandbyDeferred() == 0) { + Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() + + " parole=" + mInParole + " active=" + job.uidActive + + " exempt=" + job.getJob().isExemptedFromAppStandby()); + } + if (!mInParole + && !job.uidActive + && !job.getJob().isExemptedFromAppStandby()) { + final int bucket = job.getStandbyBucket(); + if (DEBUG_STANDBY) { + Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat + + " next=" + mNextBucketHeartbeat[bucket]); + } + if (mHeartbeat < mNextBucketHeartbeat[bucket]) { + // Only skip this job if the app is still waiting for the end of its nominal + // bucket interval. Once it's waited that long, we let it go ahead and clear. + // The final (NEVER) bucket is special; we never age those apps' jobs into + // runnability. + final long appLastRan = heartbeatWhenJobsLastRun(job); + if (bucket >= mConstants.STANDBY_BEATS.length + || (mHeartbeat > appLastRan + && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) { + // TODO: log/trace that we're deferring the job due to bucketing if we + // hit this + if (job.getWhenStandbyDeferred() == 0) { + if (DEBUG_STANDBY) { + Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " + + (appLastRan + mConstants.STANDBY_BEATS[bucket]) + + " for " + job); + } + job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); + } + return false; + } else { if (DEBUG_STANDBY) { - Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " - + (appLastRan + mConstants.STANDBY_BEATS[bucket]) - + " for " + job); + Slog.v(TAG, "Bucket deferred job aged into runnability at " + + mHeartbeat + " : " + job); } - job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); - } - return false; - } else { - if (DEBUG_STANDBY) { - Slog.v(TAG, "Bucket deferred job aged into runnability at " - + mHeartbeat + " : " + job); } } } @@ -2364,32 +2505,7 @@ public class JobSchedulerService extends com.android.server.SystemService @Override public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, boolean idle, int bucket, int reason) { - final int uid = mLocalPM.getPackageUid(packageName, - PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); - if (uid < 0) { - if (DEBUG_STANDBY) { - Slog.i(TAG, "App idle state change for unknown app " - + packageName + "/" + userId); - } - return; - } - - final int bucketIndex = standbyBucketToBucketIndex(bucket); - // update job bookkeeping out of band - BackgroundThread.getHandler().post(() -> { - if (DEBUG_STANDBY) { - Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex); - } - synchronized (mLock) { - mJobs.forEachJobForSourceUid(uid, job -> { - // double-check uid vs package name to disambiguate shared uids - if (packageName.equals(job.getSourcePackageName())) { - job.setStandbyBucket(bucketIndex); - } - }); - onControllerStateChanged(); - } - }); + // QuotaController handles this now. } @Override diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 35fc29ee69fc..6deecbd9a83b 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -77,6 +77,7 @@ public final class JobStatus { static final int CONSTRAINT_CONNECTIVITY = 1<<28; static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25; + static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22; // Soft override: ignore constraints like time that don't affect API availability @@ -192,6 +193,10 @@ public final class JobStatus { * Flag for {@link #trackingControllers}: the time controller is currently tracking this job. */ public static final int TRACKING_TIME = 1<<5; + /** + * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job. + */ + public static final int TRACKING_QUOTA = 1 << 6; /** * Bit mask of controllers that are currently tracking the job. @@ -291,6 +296,9 @@ public final class JobStatus { */ private boolean mReadyNotRestrictedInBg; + /** The job is within its quota based on its standby bucket. */ + private boolean mReadyWithinQuota; + /** Provide a handle to the service that this job will be run on. */ public int getServiceToken() { return callingUid; @@ -675,7 +683,6 @@ public final class JobStatus { return baseHeartbeat; } - // Called only by the standby monitoring code public void setStandbyBucket(int newBucket) { standbyBucket = newBucket; } @@ -876,22 +883,27 @@ public final class JobStatus { mPersistedUtcTimes = null; } + /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setBatteryNotLowConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setStorageNotLowConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setTimingDelayConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setDeadlineConstraintSatisfied(boolean state) { if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) { // The constraint was changed. Update the ready flag. @@ -901,18 +913,22 @@ public final class JobStatus { return false; } + /** @return true if the constraint was changed, false otherwise. */ boolean setIdleConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_IDLE, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setConnectivityConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setContentTriggerConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state); } + /** @return true if the constraint was changed, false otherwise. */ boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) { dozeWhitelisted = whitelisted; if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) { @@ -923,6 +939,7 @@ public final class JobStatus { return false; } + /** @return true if the constraint was changed, false otherwise. */ boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) { if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) { // The constraint was changed. Update the ready flag. @@ -932,6 +949,17 @@ public final class JobStatus { return false; } + /** @return true if the constraint was changed, false otherwise. */ + boolean setQuotaConstraintSatisfied(boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) { + // The constraint was changed. Update the ready flag. + mReadyWithinQuota = state; + return true; + } + return false; + } + + /** @return true if the state was changed, false otherwise. */ boolean setUidActive(final boolean newActiveState) { if (newActiveState != uidActive) { uidActive = newActiveState; @@ -940,6 +968,7 @@ public final class JobStatus { return false; /* unchanged */ } + /** @return true if the constraint was changed, false otherwise. */ boolean setConstraintSatisfied(int constraint, boolean state) { boolean old = (satisfiedConstraints&constraint) != 0; if (old == state) { @@ -978,9 +1007,13 @@ public final class JobStatus { * @return Whether or not this job is ready to run, based on its requirements. */ public boolean isReady() { - // Deadline constraint trumps other constraints (except for periodic jobs where deadline - // is an implementation detail. A periodic job should only run if its constraints are - // satisfied). + // Quota constraints trumps all other constraints. + if (!mReadyWithinQuota) { + return false; + } + // Deadline constraint trumps other constraints besides quota (except for periodic jobs + // where deadline is an implementation detail. A periodic job should only run if its + // constraints are satisfied). // DeviceNotDozing implicit constraint must be satisfied // NotRestrictedInBackground implicit constraint must be satisfied return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied @@ -1169,6 +1202,9 @@ public final class JobStatus { if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { pw.print(" BACKGROUND_NOT_RESTRICTED"); } + if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { + pw.print(" WITHIN_QUOTA"); + } if (constraints != 0) { pw.print(" [0x"); pw.print(Integer.toHexString(constraints)); @@ -1205,6 +1241,9 @@ public final class JobStatus { if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) { proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING); } + if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { + proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA); + } } private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) { @@ -1237,6 +1276,13 @@ public final class JobStatus { * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. */ String getBucketName() { + return bucketName(standbyBucket); + } + + /** + * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. + */ + static String bucketName(int standbyBucket) { switch (standbyBucket) { case 0: return "ACTIVE"; case 1: return "WORKING_SET"; @@ -1367,7 +1413,8 @@ public final class JobStatus { dumpConstraints(pw, satisfiedConstraints); pw.println(); pw.print(prefix); pw.print("Unsatisfied constraints:"); - dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints)); + dumpConstraints(pw, + ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); pw.println(); if (dozeWhitelisted) { pw.print(prefix); pw.println("Doze whitelisted: true"); @@ -1375,6 +1422,9 @@ public final class JobStatus { if (uidActive) { pw.print(prefix); pw.println("Uid: active"); } + if (job.isExemptedFromAppStandby()) { + pw.print(prefix); pw.println("Is exempted from app standby"); + } } if (trackingControllers != 0) { pw.print(prefix); pw.print("Tracking:"); @@ -1384,6 +1434,7 @@ public final class JobStatus { if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE"); if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE"); if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME"); + if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA"); pw.println(); } @@ -1546,8 +1597,11 @@ public final class JobStatus { if (full) { dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints); dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS, - (requiredConstraints & ~satisfiedConstraints)); + ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted); + proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive); + proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY, + job.isExemptedFromAppStandby()); } // Tracking controllers @@ -1575,6 +1629,10 @@ public final class JobStatus { proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, JobStatusDumpProto.TRACKING_TIME); } + if ((trackingControllers & TRACKING_QUOTA) != 0) { + proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, + JobStatusDumpProto.TRACKING_QUOTA); + } // Implicit constraints final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS); diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java new file mode 100644 index 000000000000..f73ffac96dfa --- /dev/null +++ b/services/core/java/com/android/server/job/controllers/QuotaController.java @@ -0,0 +1,1299 @@ +/* + * 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.job.controllers; + +import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; +import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; +import static com.android.server.job.JobSchedulerService.NEVER_INDEX; +import static com.android.server.job.JobSchedulerService.RARE_INDEX; +import static com.android.server.job.JobSchedulerService.WORKING_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AlarmManager; +import android.app.usage.UsageStatsManagerInternal; +import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateControllerProto; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Controller that tracks whether a package has exceeded its standby bucket quota. + * + * Each job in each bucket is given 10 minutes to run within its respective time window. Active + * jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window, + * frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run 10 minutes in + * a 24 hour window. The windows are rolling, so as soon as a job would have some quota based on its + * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately + * applied to it. + * + * Test: atest com.android.server.job.controllers.QuotaControllerTest + */ +public final class QuotaController extends StateController { + private static final String TAG = "JobScheduler.Quota"; + private static final boolean DEBUG = JobSchedulerService.DEBUG + || Log.isLoggable(TAG, Log.DEBUG); + + private static final long MINUTE_IN_MILLIS = 60 * 1000L; + + private static final String ALARM_TAG_CLEANUP = "*job.cleanup*"; + private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*"; + + /** + * A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object + * associations. + */ + private static class UserPackageMap<T> { + private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>(); + + public void add(int userId, @NonNull String packageName, @Nullable T obj) { + ArrayMap<String, T> data = mData.get(userId); + if (data == null) { + data = new ArrayMap<String, T>(); + mData.put(userId, data); + } + data.put(packageName, obj); + } + + @Nullable + public T get(int userId, @NonNull String packageName) { + ArrayMap<String, T> data = mData.get(userId); + if (data != null) { + return data.get(packageName); + } + return null; + } + + /** Returns the userId at the given index. */ + public int keyAt(int index) { + return mData.keyAt(index); + } + + /** Returns the package name at the given index. */ + @NonNull + public String keyAt(int userIndex, int packageIndex) { + return mData.valueAt(userIndex).keyAt(packageIndex); + } + + /** Returns the size of the outer (userId) array. */ + public int numUsers() { + return mData.size(); + } + + public int numPackagesForUser(int userId) { + ArrayMap<String, T> data = mData.get(userId); + return data == null ? 0 : data.size(); + } + + /** Returns the value T at the given user and index. */ + @Nullable + public T valueAt(int userIndex, int packageIndex) { + return mData.valueAt(userIndex).valueAt(packageIndex); + } + + public void forEach(Consumer<T> consumer) { + for (int i = numUsers() - 1; i >= 0; --i) { + ArrayMap<String, T> data = mData.valueAt(i); + for (int j = data.size() - 1; j >= 0; --j) { + consumer.accept(data.valueAt(j)); + } + } + } + } + + /** + * Standardize the output of userId-packageName combo. + */ + private static String string(int userId, String packageName) { + return "<" + userId + ">" + packageName; + } + + @VisibleForTesting + static final class Package { + public final String packageName; + public final int userId; + + Package(int userId, String packageName) { + this.userId = userId; + this.packageName = packageName; + } + + @Override + public String toString() { + return string(userId, packageName); + } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + + proto.write(StateControllerProto.QuotaController.Package.USER_ID, userId); + proto.write(StateControllerProto.QuotaController.Package.NAME, packageName); + + proto.end(token); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Package) { + Package other = (Package) obj; + return userId == other.userId && Objects.equals(packageName, other.packageName); + } else { + return false; + } + } + + @Override + public int hashCode() { + return packageName.hashCode() + userId; + } + } + + /** List of all tracked jobs keyed by source package-userId combo. */ + private final UserPackageMap<ArraySet<JobStatus>> mTrackedJobs = new UserPackageMap<>(); + + /** Timer for each package-userId combo. */ + private final UserPackageMap<Timer> mPkgTimers = new UserPackageMap<>(); + + /** List of all timing sessions for a package-userId combo, in chronological order. */ + private final UserPackageMap<List<TimingSession>> mTimingSessions = new UserPackageMap<>(); + + /** + * List of alarm listeners for each package that listen for when each package comes back within + * quota. + */ + private final UserPackageMap<QcAlarmListener> mInQuotaAlarmListeners = new UserPackageMap<>(); + + private final AlarmManager mAlarmManager; + private final ChargingTracker mChargeTracker; + private final Handler mHandler; + + private volatile boolean mInParole; + + /** + * If the QuotaController should throttle apps based on their standby bucket and job activity. + * If false, all jobs will have their CONSTRAINT_WITHIN_QUOTA bit set to true immediately and + * indefinitely. + */ + private boolean mShouldThrottle; + + /** How much time each app will have to run jobs within their standby bucket window. */ + private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; + + /** + * How much time the package should have before transitioning from out-of-quota to in-quota. + * This should not affect processing if the package is already in-quota. + */ + private long mQuotaBufferMs = 30 * 1000L; // 30 seconds + + private long mNextCleanupTimeElapsed = 0; + private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener = + new AlarmManager.OnAlarmListener() { + @Override + public void onAlarm() { + mHandler.obtainMessage(MSG_CLEAN_UP_SESSIONS).sendToTarget(); + } + }; + + /** + * The rolling window size for each standby bucket. Within each window, an app will have 10 + * minutes to run its jobs. + */ + private final long[] mBucketPeriodsMs = new long[] { + 10 * MINUTE_IN_MILLIS, // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time + 2 * 60 * MINUTE_IN_MILLIS, // 2 hours for WORKING + 8 * 60 * MINUTE_IN_MILLIS, // 8 hours for FREQUENT + 24 * 60 * MINUTE_IN_MILLIS // 24 hours for RARE + }; + + /** The maximum period any bucket can have. */ + private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS; + + /** A package has reached its quota. The message should contain a {@link Package} object. */ + private static final int MSG_REACHED_QUOTA = 0; + /** Drop any old timing sessions. */ + private static final int MSG_CLEAN_UP_SESSIONS = 1; + /** Check if a package is now within its quota. */ + private static final int MSG_CHECK_PACKAGE = 2; + + public QuotaController(JobSchedulerService service) { + super(service); + mHandler = new QcHandler(mContext.getMainLooper()); + mChargeTracker = new ChargingTracker(); + mChargeTracker.startTracking(); + mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + + // Set up the app standby bucketing tracker + UsageStatsManagerInternal usageStats = LocalServices.getService( + UsageStatsManagerInternal.class); + usageStats.addAppIdleStateChangeListener(new StandbyTracker()); + + onConstantsUpdatedLocked(); + } + + @Override + public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { + // Still need to track jobs even if mShouldThrottle is false in case it's set to true at + // some point. + ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName()); + if (jobs == null) { + jobs = new ArraySet<>(); + mTrackedJobs.add(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobs); + } + jobs.add(jobStatus); + jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA); + jobStatus.setQuotaConstraintSatisfied(!mShouldThrottle || isWithinQuotaLocked(jobStatus)); + } + + @Override + public void prepareForExecutionLocked(JobStatus jobStatus) { + if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); + final int userId = jobStatus.getSourceUserId(); + final String packageName = jobStatus.getSourcePackageName(); + Timer timer = mPkgTimers.get(userId, packageName); + if (timer == null) { + timer = new Timer(userId, packageName); + mPkgTimers.add(userId, packageName, timer); + } + timer.startTrackingJob(jobStatus); + } + + @Override + public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, + boolean forUpdate) { + if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) { + Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName()); + if (timer != null) { + timer.stopTrackingJob(jobStatus); + } + ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName()); + if (jobs != null) { + jobs.remove(jobStatus); + } + } + } + + @Override + public void onConstantsUpdatedLocked() { + boolean changed = false; + if (mShouldThrottle == mConstants.USE_HEARTBEATS) { + mShouldThrottle = !mConstants.USE_HEARTBEATS; + changed = true; + } + long newAllowedTimeMs = Math.min(MAX_PERIOD_MS, + Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS)); + if (mAllowedTimePerPeriodMs != newAllowedTimeMs) { + mAllowedTimePerPeriodMs = newAllowedTimeMs; + changed = true; + } + long newQuotaBufferMs = Math.max(0, + Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS)); + if (mQuotaBufferMs != newQuotaBufferMs) { + mQuotaBufferMs = newQuotaBufferMs; + changed = true; + } + long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS)); + if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) { + mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs; + changed = true; + } + long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS)); + if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) { + mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs; + changed = true; + } + long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS)); + if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) { + mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs; + changed = true; + } + long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS)); + if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) { + mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs; + changed = true; + } + + if (changed) { + // Update job bookkeeping out of band. + BackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + maybeUpdateAllConstraintsLocked(); + } + }); + } + } + + /** + * Returns an appropriate standby bucket for the job, taking into account any standby + * exemptions. + */ + private int getEffectiveStandbyBucket(@NonNull final JobStatus jobStatus) { + if (jobStatus.uidActive || jobStatus.getJob().isExemptedFromAppStandby()) { + // Treat these cases as if they're in the ACTIVE bucket so that they get throttled + // like other ACTIVE apps. + return ACTIVE_INDEX; + } + return jobStatus.getStandbyBucket(); + } + + private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { + final int standbyBucket = getEffectiveStandbyBucket(jobStatus); + return isWithinQuotaLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), + standbyBucket); + } + + private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, + final int standbyBucket) { + if (standbyBucket == NEVER_INDEX) return false; + if (standbyBucket == ACTIVE_INDEX) return true; + // This check is needed in case the flag is toggled after a job has been registered. + if (!mShouldThrottle) return true; + + // Quota constraint is not enforced while charging or when parole is on. + return mChargeTracker.isCharging() || mInParole + || getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0; + } + + @VisibleForTesting + long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) { + return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName(), + getEffectiveStandbyBucket(jobStatus)); + } + + @VisibleForTesting + long getRemainingExecutionTimeLocked(final int userId, @NonNull final String packageName) { + final int standbyBucket = JobSchedulerService.standbyBucketForPackage(packageName, + userId, sElapsedRealtimeClock.millis()); + return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket); + } + + /** + * Returns the amount of time, in milliseconds, that this job has remaining to run based on its + * current standby bucket. Time remaining could be negative if the app was moved from a less + * restricted to a more restricted bucket. + */ + private long getRemainingExecutionTimeLocked(final int userId, + @NonNull final String packageName, final int standbyBucket) { + if (standbyBucket == NEVER_INDEX) { + return 0; + } + final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket]; + final long trailingRunDurationMs = getTrailingExecutionTimeLocked( + userId, packageName, bucketWindowSizeMs); + return mAllowedTimePerPeriodMs - trailingRunDurationMs; + } + + /** Returns how long the uid has had jobs running within the most recent window. */ + @VisibleForTesting + long getTrailingExecutionTimeLocked(final int userId, @NonNull final String packageName, + final long windowSizeMs) { + long totalTime = 0; + + Timer timer = mPkgTimers.get(userId, packageName); + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (timer != null && timer.isActive()) { + totalTime = timer.getCurrentDuration(nowElapsed); + } + + List<TimingSession> sessions = mTimingSessions.get(userId, packageName); + if (sessions == null || sessions.size() == 0) { + return totalTime; + } + + final long startElapsed = nowElapsed - windowSizeMs; + // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get + // the most recent ones. + for (int i = sessions.size() - 1; i >= 0; --i) { + TimingSession session = sessions.get(i); + if (startElapsed < session.startTimeElapsed) { + totalTime += session.endTimeElapsed - session.startTimeElapsed; + } else if (startElapsed < session.endTimeElapsed) { + // The session started before the window but ended within the window. Only include + // the portion that was within the window. + totalTime += session.endTimeElapsed - startElapsed; + } else { + // This session ended before the window. No point in going any further. + return totalTime; + } + } + return totalTime; + } + + @VisibleForTesting + void saveTimingSession(final int userId, @NonNull final String packageName, + @NonNull final TimingSession session) { + synchronized (mLock) { + List<TimingSession> sessions = mTimingSessions.get(userId, packageName); + if (sessions == null) { + sessions = new ArrayList<>(); + mTimingSessions.add(userId, packageName, sessions); + } + sessions.add(session); + + maybeScheduleCleanupAlarmLocked(); + } + } + + private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> { + public long earliestEndElapsed = Long.MAX_VALUE; + + @Override + public void accept(List<TimingSession> sessions) { + if (sessions != null && sessions.size() > 0) { + earliestEndElapsed = Math.min(earliestEndElapsed, sessions.get(0).endTimeElapsed); + } + } + + void reset() { + earliestEndElapsed = Long.MAX_VALUE; + } + } + + private final EarliestEndTimeFunctor mEarliestEndTimeFunctor = new EarliestEndTimeFunctor(); + + /** Schedule a cleanup alarm if necessary and there isn't already one scheduled. */ + @VisibleForTesting + void maybeScheduleCleanupAlarmLocked() { + if (mNextCleanupTimeElapsed > sElapsedRealtimeClock.millis()) { + // There's already an alarm scheduled. Just stick with that one. There's no way we'll + // end up scheduling an earlier alarm. + if (DEBUG) { + Slog.v(TAG, "Not scheduling cleanup since there's already one at " + + mNextCleanupTimeElapsed + " (in " + (mNextCleanupTimeElapsed + - sElapsedRealtimeClock.millis()) + "ms)"); + } + return; + } + mEarliestEndTimeFunctor.reset(); + mTimingSessions.forEach(mEarliestEndTimeFunctor); + final long earliestEndElapsed = mEarliestEndTimeFunctor.earliestEndElapsed; + if (earliestEndElapsed == Long.MAX_VALUE) { + // Couldn't find a good time to clean up. Maybe this was called after we deleted all + // timing sessions. + if (DEBUG) Slog.d(TAG, "Didn't find a time to schedule cleanup"); + return; + } + // Need to keep sessions for all apps up to the max period, regardless of their current + // standby bucket. + long nextCleanupElapsed = earliestEndElapsed + MAX_PERIOD_MS; + if (nextCleanupElapsed - mNextCleanupTimeElapsed <= 10 * MINUTE_IN_MILLIS) { + // No need to clean up too often. Delay the alarm if the next cleanup would be too soon + // after it. + nextCleanupElapsed += 10 * MINUTE_IN_MILLIS; + } + mNextCleanupTimeElapsed = nextCleanupElapsed; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP, + mSessionCleanupAlarmListener, mHandler); + if (DEBUG) Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed); + } + + private void handleNewChargingStateLocked() { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final boolean isCharging = mChargeTracker.isCharging(); + if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging); + // Deal with Timers first. + mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging)); + // Now update jobs. + maybeUpdateAllConstraintsLocked(); + } + + private void maybeUpdateAllConstraintsLocked() { + boolean changed = false; + for (int u = 0; u < mTrackedJobs.numUsers(); ++u) { + final int userId = mTrackedJobs.keyAt(u); + for (int p = 0; p < mTrackedJobs.numPackagesForUser(userId); ++p) { + final String packageName = mTrackedJobs.keyAt(u, p); + changed |= maybeUpdateConstraintForPkgLocked(userId, packageName); + } + } + if (changed) { + mStateChangedListener.onControllerStateChanged(); + } + } + + /** + * Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package. + * + * @return true if at least one job had its bit changed + */ + private boolean maybeUpdateConstraintForPkgLocked(final int userId, + @NonNull final String packageName) { + ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); + if (jobs == null || jobs.size() == 0) { + return false; + } + + // Quota is the same for all jobs within a package. + final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket(); + final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket); + boolean changed = false; + for (int i = jobs.size() - 1; i >= 0; --i) { + final JobStatus js = jobs.valueAt(i); + if (realStandbyBucket == getEffectiveStandbyBucket(js)) { + changed |= js.setQuotaConstraintSatisfied(realInQuota); + } else { + // This job is somehow exempted. Need to determine its own quota status. + changed |= js.setQuotaConstraintSatisfied(isWithinQuotaLocked(js)); + } + } + if (!realInQuota) { + // Don't want to use the effective standby bucket here since that bump the bucket to + // ACTIVE for one of the jobs, which doesn't help with other jobs that aren't + // exempted. + maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket); + } else { + QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName); + if (alarmListener != null) { + mAlarmManager.cancel(alarmListener); + // Set the trigger time to 0 so that the alarm doesn't think it's still waiting. + alarmListener.setTriggerTime(0); + } + } + return changed; + } + + /** + * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run + * again. This should only be called if the package is already out of quota. + */ + @VisibleForTesting + void maybeScheduleStartAlarmLocked(final int userId, @NonNull final String packageName, + final int standbyBucket) { + final String pkgString = string(userId, packageName); + if (standbyBucket == NEVER_INDEX) { + return; + } else if (standbyBucket == ACTIVE_INDEX) { + // ACTIVE apps are "always" in quota. + if (DEBUG) { + Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString + + " even though it is active"); + } + mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); + + QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName); + if (alarmListener != null) { + // Cancel any pending alarm. + mAlarmManager.cancel(alarmListener); + // Set the trigger time to 0 so that the alarm doesn't think it's still waiting. + alarmListener.setTriggerTime(0); + } + return; + } + + List<TimingSession> sessions = mTimingSessions.get(userId, packageName); + if (sessions == null || sessions.size() == 0) { + // If there are no sessions, then the job is probably in quota. + if (DEBUG) { + Slog.wtf(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString + + " even though it is likely within its quota."); + } + mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); + return; + } + + final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket]; + final long nowElapsed = sElapsedRealtimeClock.millis(); + // How far back we need to look. + final long startElapsed = nowElapsed - bucketWindowSizeMs; + + long totalTime = 0; + long cutoffTimeElapsed = nowElapsed; + for (int i = sessions.size() - 1; i >= 0; i--) { + TimingSession session = sessions.get(i); + if (startElapsed < session.startTimeElapsed) { + cutoffTimeElapsed = session.startTimeElapsed; + totalTime += session.endTimeElapsed - session.startTimeElapsed; + } else if (startElapsed < session.endTimeElapsed) { + // The session started before the window but ended within the window. Only + // include the portion that was within the window. + cutoffTimeElapsed = startElapsed; + totalTime += session.endTimeElapsed - startElapsed; + } else { + // This session ended before the window. No point in going any further. + break; + } + if (totalTime >= mAllowedTimePerPeriodMs) { + break; + } + } + if (totalTime < mAllowedTimePerPeriodMs) { + // Already in quota. Why was this method called? + if (DEBUG) { + Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString + + " even though it already has " + (mAllowedTimePerPeriodMs - totalTime) + + "ms in its quota."); + } + mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); + return; + } + + QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName); + if (alarmListener == null) { + alarmListener = new QcAlarmListener(userId, packageName); + mInQuotaAlarmListeners.add(userId, packageName, alarmListener); + } + + // We add all the way back to the beginning of a session (or the window) even when we don't + // need to (in order to simplify the for loop above), so there might be some extra we + // need to add back. + final long extraTimeMs = totalTime - mAllowedTimePerPeriodMs; + // The time this app will have quota again. + final long inQuotaTimeElapsed = + cutoffTimeElapsed + extraTimeMs + mQuotaBufferMs + bucketWindowSizeMs; + // Only schedule the alarm if: + // 1. There isn't one currently scheduled + // 2. The new alarm is significantly earlier than the previous alarm (which could be the + // case if the package moves into a higher standby bucket). If it's earlier but not + // significantly so, then we essentially delay the job a few extra minutes. + // 3. The alarm is after the current alarm by more than the quota buffer. + // TODO: this might be overengineering. Simplify if proven safe. + if (!alarmListener.isWaiting() + || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS + || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed - mQuotaBufferMs) { + if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString); + // If the next time this app will have quota is at least 3 minutes before the + // alarm is supposed to go off, reschedule the alarm. + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed, + ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler); + alarmListener.setTriggerTime(inQuotaTimeElapsed); + } + } + + private final class ChargingTracker extends BroadcastReceiver { + /** + * Track whether we're charging. This has a slightly different definition than that of + * BatteryController. + */ + private boolean mCharging; + + ChargingTracker() { + } + + public void startTracking() { + IntentFilter filter = new IntentFilter(); + + // Charging/not charging. + filter.addAction(BatteryManager.ACTION_CHARGING); + filter.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(this, filter); + + // Initialise tracker state. + BatteryManagerInternal batteryManagerInternal = + LocalServices.getService(BatteryManagerInternal.class); + mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); + } + + public boolean isCharging() { + return mCharging; + } + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + final String action = intent.getAction(); + if (BatteryManager.ACTION_CHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Received charging intent, fired @ " + + sElapsedRealtimeClock.millis()); + } + mCharging = true; + handleNewChargingStateLocked(); + } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Disconnected from power."); + } + mCharging = false; + handleNewChargingStateLocked(); + } + } + } + } + + @VisibleForTesting + static final class TimingSession { + // Start timestamp in elapsed realtime timebase. + public final long startTimeElapsed; + // End timestamp in elapsed realtime timebase. + public final long endTimeElapsed; + // How many jobs ran during this session. + public final int jobCount; + + TimingSession(long startElapsed, long endElapsed, int jobCount) { + this.startTimeElapsed = startElapsed; + this.endTimeElapsed = endElapsed; + this.jobCount = jobCount; + } + + @Override + public String toString() { + return "TimingSession{" + startTimeElapsed + "->" + endTimeElapsed + ", " + jobCount + + "}"; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TimingSession) { + TimingSession other = (TimingSession) obj; + return startTimeElapsed == other.startTimeElapsed + && endTimeElapsed == other.endTimeElapsed + && jobCount == other.jobCount; + } else { + return false; + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(new long[] {startTimeElapsed, endTimeElapsed, jobCount}); + } + + public void dump(IndentingPrintWriter pw) { + pw.print(startTimeElapsed); + pw.print(" -> "); + pw.print(endTimeElapsed); + pw.print(" ("); + pw.print(endTimeElapsed - startTimeElapsed); + pw.print("), "); + pw.print(jobCount); + pw.print(" jobs."); + pw.println(); + } + + public void dump(@NonNull ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + + proto.write(StateControllerProto.QuotaController.TimingSession.START_TIME_ELAPSED, + startTimeElapsed); + proto.write(StateControllerProto.QuotaController.TimingSession.END_TIME_ELAPSED, + endTimeElapsed); + proto.write(StateControllerProto.QuotaController.TimingSession.JOB_COUNT, jobCount); + + proto.end(token); + } + } + + private final class Timer { + private final Package mPkg; + + // List of jobs currently running for this package. + private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>(); + private long mStartTimeElapsed; + private int mJobCount; + + Timer(int userId, String packageName) { + mPkg = new Package(userId, packageName); + } + + void startTrackingJob(@NonNull JobStatus jobStatus) { + if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString()); + synchronized (mLock) { + // Always track jobs, even when charging. + mRunningJobs.add(jobStatus); + if (!mChargeTracker.isCharging()) { + mJobCount++; + if (mRunningJobs.size() == 1) { + // Started tracking the first job. + mStartTimeElapsed = sElapsedRealtimeClock.millis(); + scheduleCutoff(); + } + } + } + } + + void stopTrackingJob(@NonNull JobStatus jobStatus) { + if (DEBUG) Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString()); + synchronized (mLock) { + if (mRunningJobs.size() == 0) { + // maybeStopTrackingJobLocked can be called when an app cancels a job, so a + // timer may not be running when it's asked to stop tracking a job. + if (DEBUG) { + Slog.d(TAG, "Timer isn't tracking any jobs but still told to stop"); + } + return; + } + mRunningJobs.remove(jobStatus); + if (!mChargeTracker.isCharging() && mRunningJobs.size() == 0) { + emitSessionLocked(sElapsedRealtimeClock.millis()); + cancelCutoff(); + } + } + } + + private void emitSessionLocked(long nowElapsed) { + if (mJobCount <= 0) { + // Nothing to emit. + return; + } + TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mJobCount); + saveTimingSession(mPkg.userId, mPkg.packageName, ts); + mJobCount = 0; + // Don't reset the tracked jobs list as we need to keep tracking the current number + // of jobs. + // However, cancel the currently scheduled cutoff since it's not currently useful. + cancelCutoff(); + } + + /** + * Returns true if the Timer is actively tracking, as opposed to passively ref counting + * during charging. + */ + public boolean isActive() { + synchronized (mLock) { + return mJobCount > 0; + } + } + + long getCurrentDuration(long nowElapsed) { + synchronized (mLock) { + return !isActive() ? 0 : nowElapsed - mStartTimeElapsed; + } + } + + void onChargingChanged(long nowElapsed, boolean isCharging) { + synchronized (mLock) { + if (isCharging) { + emitSessionLocked(nowElapsed); + } else { + // Start timing from unplug. + if (mRunningJobs.size() > 0) { + mStartTimeElapsed = nowElapsed; + // NOTE: this does have the unfortunate consequence that if the device is + // repeatedly plugged in and unplugged, the job count for a package may be + // artificially high. + mJobCount = mRunningJobs.size(); + // Schedule cutoff since we're now actively tracking for quotas again. + scheduleCutoff(); + } + } + } + } + + void rescheduleCutoff() { + cancelCutoff(); + scheduleCutoff(); + } + + private void scheduleCutoff() { + // Each package can only be in one standby bucket, so we only need to have one + // message per timer. We only need to reschedule when restarting timer or when + // standby bucket changes. + synchronized (mLock) { + if (!isActive()) { + return; + } + Message msg = mHandler.obtainMessage(MSG_REACHED_QUOTA, mPkg); + final long timeRemainingMs = getRemainingExecutionTimeLocked(mPkg.userId, + mPkg.packageName); + if (DEBUG) { + Slog.i(TAG, "Job for " + mPkg + " has " + timeRemainingMs + "ms left."); + } + // If the job was running the entire time, then the system would be up, so it's + // fine to use uptime millis for these messages. + mHandler.sendMessageDelayed(msg, timeRemainingMs); + } + } + + private void cancelCutoff() { + mHandler.removeMessages(MSG_REACHED_QUOTA, mPkg); + } + + public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { + pw.print("Timer{"); + pw.print(mPkg); + pw.print("} "); + if (isActive()) { + pw.print("started at "); + pw.print(mStartTimeElapsed); + } else { + pw.print("NOT active"); + } + pw.print(", "); + pw.print(mJobCount); + pw.print(" running jobs"); + pw.println(); + pw.increaseIndent(); + for (int i = 0; i < mRunningJobs.size(); i++) { + JobStatus js = mRunningJobs.valueAt(i); + if (predicate.test(js)) { + pw.println(js.toShortString()); + } + } + + pw.decreaseIndent(); + } + + public void dump(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) { + final long token = proto.start(fieldId); + + mPkg.writeToProto(proto, StateControllerProto.QuotaController.Timer.PKG); + proto.write(StateControllerProto.QuotaController.Timer.IS_ACTIVE, isActive()); + proto.write(StateControllerProto.QuotaController.Timer.START_TIME_ELAPSED, + mStartTimeElapsed); + proto.write(StateControllerProto.QuotaController.Timer.JOB_COUNT, mJobCount); + for (int i = 0; i < mRunningJobs.size(); i++) { + JobStatus js = mRunningJobs.valueAt(i); + if (predicate.test(js)) { + js.writeToShortProto(proto, + StateControllerProto.QuotaController.Timer.RUNNING_JOBS); + } + } + + proto.end(token); + } + } + + /** + * Tracking of app assignments to standby buckets + */ + final class StandbyTracker extends AppIdleStateChangeListener { + + @Override + public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, + boolean idle, int bucket, int reason) { + // Update job bookkeeping out of band. + BackgroundThread.getHandler().post(() -> { + final int bucketIndex = JobSchedulerService.standbyBucketToBucketIndex(bucket); + if (DEBUG) { + Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex " + + bucketIndex); + } + synchronized (mLock) { + ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); + if (jobs == null || jobs.size() == 0) { + return; + } + for (int i = jobs.size() - 1; i >= 0; i--) { + JobStatus js = jobs.valueAt(i); + js.setStandbyBucket(bucketIndex); + } + Timer timer = mPkgTimers.get(userId, packageName); + if (timer != null && timer.isActive()) { + timer.rescheduleCutoff(); + } + if (!mShouldThrottle || maybeUpdateConstraintForPkgLocked(userId, + packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + } + }); + } + + @Override + public void onParoleStateChanged(final boolean isParoleOn) { + mInParole = isParoleOn; + if (DEBUG) Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF")); + // Update job bookkeeping out of band. + BackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + maybeUpdateAllConstraintsLocked(); + } + }); + } + } + + private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> { + private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() { + public boolean test(TimingSession ts) { + return ts.endTimeElapsed <= sElapsedRealtimeClock.millis() - MAX_PERIOD_MS; + } + }; + + @Override + public void accept(List<TimingSession> sessions) { + if (sessions != null) { + // Remove everything older than MAX_PERIOD_MS time ago. + sessions.removeIf(mTooOld); + } + } + } + + private final DeleteTimingSessionsFunctor mDeleteOldSessionsFunctor = + new DeleteTimingSessionsFunctor(); + + @VisibleForTesting + void deleteObsoleteSessionsLocked() { + mTimingSessions.forEach(mDeleteOldSessionsFunctor); + } + + private class QcHandler extends Handler { + QcHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + synchronized (mLock) { + switch (msg.what) { + case MSG_REACHED_QUOTA: { + Package pkg = (Package) msg.obj; + if (DEBUG) Slog.d(TAG, "Checking if " + pkg + " has reached its quota."); + + long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId, + pkg.packageName); + if (timeRemainingMs <= 50) { + // Less than 50 milliseconds left. Start process of shutting down jobs. + if (DEBUG) Slog.d(TAG, pkg + " has reached its quota."); + if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + } else { + // This could potentially happen if an old session phases out while a + // job is currently running. + // Reschedule message + Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg); + if (DEBUG) { + Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left."); + } + sendMessageDelayed(rescheduleMsg, timeRemainingMs); + } + break; + } + case MSG_CLEAN_UP_SESSIONS: + if (DEBUG) Slog.d(TAG, "Cleaning up timing sessions."); + deleteObsoleteSessionsLocked(); + maybeScheduleCleanupAlarmLocked(); + + break; + case MSG_CHECK_PACKAGE: { + String packageName = (String) msg.obj; + int userId = msg.arg1; + if (DEBUG) Slog.d(TAG, "Checking pkg " + string(userId, packageName)); + if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + break; + } + } + } + } + } + + private class QcAlarmListener implements AlarmManager.OnAlarmListener { + private final int mUserId; + private final String mPackageName; + private volatile long mTriggerTimeElapsed; + + QcAlarmListener(int userId, String packageName) { + mUserId = userId; + mPackageName = packageName; + } + + boolean isWaiting() { + return mTriggerTimeElapsed > 0; + } + + void setTriggerTime(long timeElapsed) { + mTriggerTimeElapsed = timeElapsed; + } + + long getTriggerTimeElapsed() { + return mTriggerTimeElapsed; + } + + @Override + public void onAlarm() { + mHandler.obtainMessage(MSG_CHECK_PACKAGE, mUserId, 0, mPackageName).sendToTarget(); + mTriggerTimeElapsed = 0; + } + } + + //////////////////////// TESTING HELPERS ///////////////////////////// + + @VisibleForTesting + long getAllowedTimePerPeriodMs() { + return mAllowedTimePerPeriodMs; + } + + @VisibleForTesting + @NonNull + long[] getBucketWindowSizes() { + return mBucketPeriodsMs; + } + + @VisibleForTesting + @NonNull + Handler getHandler() { + return mHandler; + } + + @VisibleForTesting + long getInQuotaBufferMs() { + return mQuotaBufferMs; + } + + @VisibleForTesting + @Nullable + List<TimingSession> getTimingSessions(int userId, String packageName) { + return mTimingSessions.get(userId, packageName); + } + + //////////////////////////// DATA DUMP ////////////////////////////// + + @Override + public void dumpControllerStateLocked(final IndentingPrintWriter pw, + final Predicate<JobStatus> predicate) { + pw.println("Is throttling: " + mShouldThrottle); + pw.println("Is charging: " + mChargeTracker.isCharging()); + pw.println("In parole: " + mInParole); + pw.println(); + + mTrackedJobs.forEach((jobs) -> { + for (int j = 0; j < jobs.size(); j++) { + final JobStatus js = jobs.valueAt(j); + if (!predicate.test(js)) { + continue; + } + pw.print("#"); + js.printUniqueId(pw); + pw.print(" from "); + UserHandle.formatUid(pw, js.getSourceUid()); + pw.println(); + + pw.increaseIndent(); + pw.print(JobStatus.bucketName(getEffectiveStandbyBucket(js))); + pw.print(", "); + if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { + pw.print("within quota"); + } else { + pw.print("not within quota"); + } + pw.print(", "); + pw.print(getRemainingExecutionTimeLocked(js)); + pw.print("ms remaining in quota"); + pw.decreaseIndent(); + pw.println(); + } + }); + + pw.println(); + for (int u = 0; u < mPkgTimers.numUsers(); ++u) { + final int userId = mPkgTimers.keyAt(u); + for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) { + final String pkgName = mPkgTimers.keyAt(u, p); + mPkgTimers.valueAt(u, p).dump(pw, predicate); + pw.println(); + List<TimingSession> sessions = mTimingSessions.get(userId, pkgName); + if (sessions != null) { + pw.increaseIndent(); + pw.println("Saved sessions:"); + pw.increaseIndent(); + for (int j = sessions.size() - 1; j >= 0; j--) { + TimingSession session = sessions.get(j); + session.dump(pw); + } + pw.decreaseIndent(); + pw.decreaseIndent(); + pw.println(); + } + } + } + } + + @Override + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate<JobStatus> predicate) { + final long token = proto.start(fieldId); + final long mToken = proto.start(StateControllerProto.QUOTA); + + proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging()); + proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole); + + mTrackedJobs.forEach((jobs) -> { + for (int j = 0; j < jobs.size(); j++) { + final JobStatus js = jobs.valueAt(j); + if (!predicate.test(js)) { + continue; + } + final long jsToken = proto.start( + StateControllerProto.QuotaController.TRACKED_JOBS); + js.writeToShortProto(proto, + StateControllerProto.QuotaController.TrackedJob.INFO); + proto.write(StateControllerProto.QuotaController.TrackedJob.SOURCE_UID, + js.getSourceUid()); + proto.write( + StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET, + getEffectiveStandbyBucket(js)); + proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA, + js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS, + getRemainingExecutionTimeLocked(js)); + proto.end(jsToken); + } + }); + + for (int u = 0; u < mPkgTimers.numUsers(); ++u) { + final int userId = mPkgTimers.keyAt(u); + for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) { + final String pkgName = mPkgTimers.keyAt(u, p); + final long psToken = proto.start( + StateControllerProto.QuotaController.PACKAGE_STATS); + mPkgTimers.valueAt(u, p).dump(proto, + StateControllerProto.QuotaController.PackageStats.TIMER, predicate); + + List<TimingSession> sessions = mTimingSessions.get(userId, pkgName); + if (sessions != null) { + for (int j = sessions.size() - 1; j >= 0; j--) { + TimingSession session = sessions.get(j); + session.dump(proto, + StateControllerProto.QuotaController.PackageStats.SAVED_SESSIONS); + } + } + + proto.end(psToken); + } + } + + proto.end(mToken); + proto.end(token); + } +} diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index c2be28336406..b439c0ddd028 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -53,22 +53,31 @@ public abstract class StateController { * preexisting tasks. */ public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob); + /** * Optionally implement logic here to prepare the job to be executed. */ public void prepareForExecutionLocked(JobStatus jobStatus) { } + /** * Remove task - this will happen if the task is cancelled, completed, etc. */ public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate); + /** * Called when a new job is being created to reschedule an old failed job. */ public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) { } + /** + * Called when the JobScheduler.Constants are updated. + */ + public void onConstantsUpdatedLocked() { + } + public abstract void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate); public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index 2584187fffe6..9b9f4de7a18f 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -15,12 +15,15 @@ */ package com.android.server.notification; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; + import android.app.Notification; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.provider.Settings; import android.telecom.TelecomManager; import com.android.internal.util.NotificationMessagingUtil; @@ -47,6 +50,21 @@ public class NotificationComparator @Override public int compare(NotificationRecord left, NotificationRecord right) { + final int leftImportance = left.getImportance(); + final int rightImportance = right.getImportance(); + final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT; + final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT; + + // With new interruption model, prefer importance bucket above all other criteria + // (to ensure buckets are contiguous) + if (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) { + if (isLeftHighImportance != isRightHighImportance) { + // by importance bucket, high importance higher than low importance + return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance); + } + } + // first all colorized notifications boolean leftImportantColorized = isImportantColorized(left); boolean rightImportantColorized = isImportantColorized(right); @@ -86,8 +104,6 @@ public class NotificationComparator return -1 * Boolean.compare(leftPeople, rightPeople); } - final int leftImportance = left.getImportance(); - final int rightImportance = right.getImportance(); if (leftImportance != rightImportance) { // by importance, high to low return -1 * Integer.compare(leftImportance, rightImportance); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6c2549e9b04d..95e1962fdbfb 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -207,7 +207,6 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; -import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.lights.Light; @@ -266,7 +265,7 @@ public class NotificationManagerService extends SystemService { // message codes static final int MESSAGE_DURATION_REACHED = 2; - // 3: removed to a different handler + static final int MESSAGE_SAVE_POLICY_FILE = 3; static final int MESSAGE_SEND_RANKING_UPDATE = 4; static final int MESSAGE_LISTENER_HINTS_CHANGED = 5; static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6; @@ -428,6 +427,7 @@ public class NotificationManagerService extends SystemService { private GroupHelper mGroupHelper; private int mAutoGroupAtCount; private boolean mIsTelevision; + private boolean mIsAutomotive; private MetricsLogger mMetricsLogger; private Predicate<String> mAllowedManagedServicePackages; @@ -573,7 +573,7 @@ public class NotificationManagerService extends SystemService { mListeners.migrateToXml(); mAssistants.migrateToXml(); mConditionProviders.migrateToXml(); - handleSavePolicyFile(); + savePolicyFile(); } mAssistants.ensureAssistant(); @@ -603,28 +603,34 @@ public class NotificationManagerService extends SystemService { } } + /** + * Saves notification policy + */ + public void savePolicyFile() { + mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); + mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); + } + private void handleSavePolicyFile() { - IoThread.getHandler().post(() -> { - if (DBG) Slog.d(TAG, "handleSavePolicyFile"); - synchronized (mPolicyFile) { - final FileOutputStream stream; - try { - stream = mPolicyFile.startWrite(); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file", e); - return; - } + if (DBG) Slog.d(TAG, "handleSavePolicyFile"); + synchronized (mPolicyFile) { + final FileOutputStream stream; + try { + stream = mPolicyFile.startWrite(); + } catch (IOException e) { + Slog.w(TAG, "Failed to save policy file", e); + return; + } - try { - writePolicyXml(stream, false /*forBackup*/); - mPolicyFile.finishWrite(stream); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file, restoring backup", e); - mPolicyFile.failWrite(stream); - } + try { + writePolicyXml(stream, false /*forBackup*/); + mPolicyFile.finishWrite(stream); + } catch (IOException e) { + Slog.w(TAG, "Failed to save policy file, restoring backup", e); + mPolicyFile.failWrite(stream); } - BackupManager.dataChanged(getContext().getPackageName()); - }); + } + BackupManager.dataChanged(getContext().getPackageName()); } private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException { @@ -1132,8 +1138,8 @@ public class NotificationManagerService extends SystemService { } } - mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList); + savePolicyFile(); } } }; @@ -1200,7 +1206,7 @@ public class NotificationManagerService extends SystemService { mListeners.onUserRemoved(userId); mConditionProviders.onUserRemoved(userId); mAssistants.onUserRemoved(userId); - handleSavePolicyFile(); + savePolicyFile(); } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); @@ -1388,6 +1394,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setIsAutomotive(boolean isAutomotive) { + mIsAutomotive = isAutomotive; + } + + @VisibleForTesting void setIsTelevision(boolean isTelevision) { mIsTelevision = isTelevision; } @@ -1451,7 +1462,7 @@ public class NotificationManagerService extends SystemService { mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override public void onConfigChanged() { - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -1543,6 +1554,9 @@ public class NotificationManagerService extends SystemService { mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK) || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION); + + mIsAutomotive = + mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0); } @Override @@ -1750,7 +1764,7 @@ public class NotificationManagerService extends SystemService { modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); } - handleSavePolicyFile(); + savePolicyFile(); } private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate, @@ -2218,7 +2232,7 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "Can't notify app about app block change", e); } - handleSavePolicyFile(); + savePolicyFile(); } /** @@ -2275,7 +2289,7 @@ public class NotificationManagerService extends SystemService { public void setShowBadge(String pkg, int uid, boolean showBadge) { checkCallerIsSystem(); mPreferencesHelper.setShowBadge(pkg, uid, showBadge); - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -2291,7 +2305,7 @@ public class NotificationManagerService extends SystemService { if (info != null) { mPreferencesHelper.setNotificationDelegate( callingPkg, callingUid, delegate, info.uid); - handleSavePolicyFile(); + savePolicyFile(); } } catch (RemoteException e) { // :( @@ -2302,7 +2316,7 @@ public class NotificationManagerService extends SystemService { public void revokeNotificationDelegate(String callingPkg) { checkCallerIsSameApp(callingPkg); mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid()); - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -2337,7 +2351,7 @@ public class NotificationManagerService extends SystemService { NotificationChannelGroup group) throws RemoteException { enforceSystemOrSystemUI("Caller not system or systemui"); createNotificationChannelGroup(pkg, uid, group, false, false); - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -2350,7 +2364,7 @@ public class NotificationManagerService extends SystemService { final NotificationChannelGroup group = groups.get(i); createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false); } - handleSavePolicyFile(); + savePolicyFile(); } private void createNotificationChannelsImpl(String pkg, int uid, @@ -2368,7 +2382,7 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); } - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -2413,7 +2427,7 @@ public class NotificationManagerService extends SystemService { UserHandle.getUserHandleForUid(callingUid), mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true), NOTIFICATION_CHANNEL_OR_GROUP_DELETED); - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -2455,7 +2469,7 @@ public class NotificationManagerService extends SystemService { mListeners.notifyNotificationChannelGroupChanged( pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete, NOTIFICATION_CHANNEL_OR_GROUP_DELETED); - handleSavePolicyFile(); + savePolicyFile(); } } @@ -2588,7 +2602,7 @@ public class NotificationManagerService extends SystemService { true, UserHandle.getCallingUserId(), packages, uids); } - handleSavePolicyFile(); + savePolicyFile(); } @@ -3376,7 +3390,7 @@ public class NotificationManagerService extends SystemService { final ByteArrayInputStream bais = new ByteArrayInputStream(payload); try { readPolicyXml(bais, true /*forRestore*/); - handleSavePolicyFile(); + savePolicyFile(); } catch (NumberFormatException | XmlPullParserException | IOException e) { Slog.w(TAG, "applyRestore: error reading payload", e); } @@ -3417,7 +3431,7 @@ public class NotificationManagerService extends SystemService { .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - handleSavePolicyFile(); + savePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3561,7 +3575,7 @@ public class NotificationManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - handleSavePolicyFile(); + savePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3587,7 +3601,7 @@ public class NotificationManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - handleSavePolicyFile(); + savePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3670,7 +3684,7 @@ public class NotificationManagerService extends SystemService { verifyPrivilegedListener(token, user, false); createNotificationChannelGroup( pkg, getUidForPackageAndUser(pkg, user), group, false, true); - handleSavePolicyFile(); + savePolicyFile(); } @Override @@ -3719,7 +3733,7 @@ public class NotificationManagerService extends SystemService { } if (allow != mLockScreenAllowSecureNotifications) { mLockScreenAllowSecureNotifications = allow; - handleSavePolicyFile(); + savePolicyFile(); } } @@ -4478,7 +4492,7 @@ public class NotificationManagerService extends SystemService { Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey()); } mSnoozeHelper.update(userId, r); - handleSavePolicyFile(); + savePolicyFile(); return false; } @@ -4609,7 +4623,7 @@ public class NotificationManagerService extends SystemService { mSnoozeHelper.snooze(r, mDuration); } r.recordSnoozed(); - handleSavePolicyFile(); + savePolicyFile(); } } @@ -4687,7 +4701,7 @@ public class NotificationManagerService extends SystemService { if (mReason != REASON_SNOOZED) { final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId); if (wasSnoozed) { - handleSavePolicyFile(); + savePolicyFile(); } } } @@ -5131,7 +5145,9 @@ public class NotificationManagerService extends SystemService { // Should this notification make noise, vibe, or use the LED? final boolean aboveThreshold = - record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT; + mIsAutomotive + ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT + : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT; // Remember if this notification already owns the notification channels. boolean wasBeep = key != null && key.equals(mSoundNotificationKey); @@ -5729,6 +5745,9 @@ public class NotificationManagerService extends SystemService { case MESSAGE_FINISH_TOKEN_TIMEOUT: handleKillTokenTimeout((ToastRecord) msg.obj); break; + case MESSAGE_SAVE_POLICY_FILE: + handleSavePolicyFile(); + break; case MESSAGE_SEND_RANKING_UPDATE: handleSendRankingUpdate(); break; @@ -6246,7 +6265,7 @@ public class NotificationManagerService extends SystemService { Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName)); } mSnoozeHelper.repost(key); - handleSavePolicyFile(); + savePolicyFile(); } @GuardedBy("mNotificationLock") diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8abb5000d544..dab4e79e141c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -58,6 +58,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; import static android.content.pm.PackageManager.INSTALL_INTERNAL; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; @@ -10299,12 +10300,21 @@ public class PackageManagerService extends IPackageManager.Stub compareSignatures( signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures, pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) { - // Treat mismatched signatures on system packages using a shared UID as - // fatal for the system overall, rather than just failing to install - // whichever package happened to be scanned later. - throw new IllegalStateException( - "Signature mismatch on system package " + pkg.packageName - + " for shared user " + pkgSetting.sharedUser); + if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) { + // Mismatched signatures is an error and silently skipping system + // packages will likely break the device in unforeseen ways. However, + // we allow the device to boot anyway because, prior to P, vendors were + // not expecting the platform to crash in this situation. + throw new PackageManagerException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + "Signature mismatch for shared user: " + pkgSetting.sharedUser); + } else { + // Treat mismatched signatures on system packages using a shared UID as + // fatal for the system overall, rather than just failing to install + // whichever package happened to be scanned later. + throw new IllegalStateException("Signature mismatch on system package " + + pkg.packageName + " for shared user " + pkgSetting.sharedUser); + } } signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails; diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java new file mode 100644 index 000000000000..7ce071faab04 --- /dev/null +++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java @@ -0,0 +1,28 @@ +/* + * 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.signedconfig; + +import android.content.Context; + +class SignedConfigApplicator { + + static void applyConfig(Context context, String config, String signature) { + //TODO verify signature + //TODO parse & apply config + } + +} diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java new file mode 100644 index 000000000000..148568628397 --- /dev/null +++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java @@ -0,0 +1,105 @@ +/* + * 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.signedconfig; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.net.Uri; +import android.os.Bundle; +import android.util.Slog; + +import com.android.server.LocalServices; + +/** + * Signed config service. This is not an Android Service, but just owns a broadcast receiver for + * receiving package install and update notifications from the package manager. + */ +public class SignedConfigService { + + private static final boolean DBG = false; + private static final String TAG = "SignedConfig"; + + // TODO should these be elsewhere? In a public API? + private static final String KEY_CONFIG = "android.signedconfig"; + private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature"; + + private static class UpdateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + new SignedConfigService(context).handlePackageBroadcast(intent); + } + } + + private final Context mContext; + private final PackageManagerInternal mPacMan; + + public SignedConfigService(Context context) { + mContext = context; + mPacMan = LocalServices.getService(PackageManagerInternal.class); + } + + void handlePackageBroadcast(Intent intent) { + if (DBG) Slog.d(TAG, "handlePackageBroadcast " + intent); + Uri packageData = intent.getData(); + String packageName = packageData == null ? null : packageData.getSchemeSpecificPart(); + if (DBG) Slog.d(TAG, "handlePackageBroadcast package=" + packageName); + if (packageName == null) { + return; + } + int userId = mContext.getUser().getIdentifier(); + PackageInfo pi = mPacMan.getPackageInfo(packageName, PackageManager.GET_META_DATA, + android.os.Process.SYSTEM_UID, userId); + if (pi == null) { + Slog.w(TAG, "Got null PackageInfo for " + packageName + "; user " + userId); + return; + } + Bundle metaData = pi.applicationInfo.metaData; + if (metaData == null) { + if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata"); + return; + } + if (metaData.containsKey(KEY_CONFIG) + && metaData.containsKey(KEY_CONFIG_SIGNATURE)) { + String config = metaData.getString(KEY_CONFIG); + String signature = metaData.getString(KEY_CONFIG_SIGNATURE); + if (DBG) { + Slog.d(TAG, "Got signed config: " + config); + Slog.d(TAG, "Got config signature: " + signature); + } + SignedConfigApplicator.applyConfig(mContext, config, signature); + } else { + if (DBG) Slog.d(TAG, "Package has no config/signature."); + } + } + + /** + * Register to receive broadcasts from the package manager. + */ + public static void registerUpdateReceiver(Context context) { + if (DBG) Slog.d(TAG, "Registering receiver"); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); + filter.addDataScheme("package"); + context.registerReceiver(new UpdateReceiver(), filter); + } +} diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index e645b84e83a0..6e4c00eed181 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -17,6 +17,7 @@ package com.android.server.statusbar; import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; +import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityThread; import android.app.Notification; @@ -25,6 +26,8 @@ import android.content.ComponentName; import android.content.Context; import android.graphics.Rect; import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -40,7 +43,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; -import android.view.Display; +import android.util.SparseArray; import com.android.internal.R; import com.android.internal.statusbar.IStatusBar; @@ -63,7 +66,7 @@ import java.util.List; * A note on locking: We rely on the fact that calls onto mBar are oneway or * if they are local, that they just enqueue messages to not deadlock. */ -public class StatusBarManagerService extends IStatusBarService.Stub { +public class StatusBarManagerService extends IStatusBarService.Stub implements DisplayListener { private static final String TAG = "StatusBarManagerService"; private static final boolean SPEW = false; @@ -79,24 +82,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub { private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener; private IBinder mSysUiVisToken = new Binder(); - private int mDisabled1 = 0; - private int mDisabled2 = 0; private final Object mLock = new Object(); private final DeathRecipient mDeathRecipient = new DeathRecipient(); - // encompasses lights-out mode and other flags defined on View - private int mSystemUiVisibility = 0; - private int mFullscreenStackSysUiVisibility; - private int mDockedStackSysUiVisibility; - private final Rect mFullscreenStackBounds = new Rect(); - private final Rect mDockedStackBounds = new Rect(); - private boolean mMenuVisible = false; - private int mImeWindowVis = 0; - private int mImeBackDisposition; - private boolean mShowImeSwitcher; - private IBinder mImeToken = null; private int mCurrentUserId; + private SparseArray<UiState> mDisplayUiState = new SparseArray<>(); + private class DeathRecipient implements IBinder.DeathRecipient { public void binderDied() { mBar.asBinder().unlinkToDeath(this,0); @@ -185,8 +177,29 @@ public class StatusBarManagerService extends IStatusBarService.Stub { LocalServices.addService(StatusBarManagerInternal.class, mInternalService); LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider); + + // We always have a default display. + final UiState state = new UiState(); + mDisplayUiState.put(DEFAULT_DISPLAY, state); + + final DisplayManager displayManager = + (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); + displayManager.registerDisplayListener(this, mHandler); } + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) { + synchronized (mLock) { + mDisplayUiState.remove(displayId); + } + } + + @Override + public void onDisplayChanged(int displayId) {} + /** * Private API used by NotificationManagerService. */ @@ -240,22 +253,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void topAppWindowChanged(int displayId, boolean menuVisible) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } - StatusBarManagerService.this.topAppWindowChanged(menuVisible); + StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible); } @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } - StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis, + StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis, dockedStackVis, mask, fullscreenBounds, dockedBounds, cause); } @@ -272,13 +277,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void appTransitionFinished(int displayId) { enforceStatusBarService(); - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } if (mBar != null) { try { - mBar.appTransitionFinished(); + mBar.appTransitionFinished(displayId); } catch (RemoteException ex) {} } } @@ -374,39 +375,27 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void setWindowState(int displayId, int window, int state) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } if (mBar != null) { try { - mBar.setWindowState(window, state); + mBar.setWindowState(displayId, window, state); } catch (RemoteException ex) {} } } @Override public void appTransitionPending(int displayId) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } if (mBar != null) { try { - mBar.appTransitionPending(); + mBar.appTransitionPending(displayId); } catch (RemoteException ex) {} } } @Override public void appTransitionCancelled(int displayId) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } if (mBar != null) { try { - mBar.appTransitionCancelled(); + mBar.appTransitionCancelled(displayId); } catch (RemoteException ex) {} } } @@ -414,14 +403,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime, long statusBarAnimationsDuration) { - if (displayId != Display.DEFAULT_DISPLAY) { - // TODO (b/117478341): Resolve one status bar/ navigation bar assumption - return; - } if (mBar != null) { try { mBar.appTransitionStarting( - statusBarAnimationsStartTime, statusBarAnimationsDuration); + displayId, statusBarAnimationsStartTime, statusBarAnimationsDuration); } catch (RemoteException ex) {} } } @@ -449,6 +434,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { return false; } + // TODO(b/118592525): support it per display if necessary. @Override public void onProposedRotationChanged(int rotation, boolean isValid) { if (mBar != null){ @@ -462,7 +448,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub { private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() { @Override public boolean isGlobalActionsDisabled() { - return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0; + // TODO(b/118592525): support global actions for multi-display. + final int disabled2 = mDisplayUiState.get(DEFAULT_DISPLAY).getDisabled2(); + return (disabled2 & DISABLE2_GLOBAL_ACTIONS) != 0; } @Override @@ -664,20 +652,23 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } + // TODO(b/117478341): make it aware of multi-display if needed. @Override public void disable(int what, IBinder token, String pkg) { disableForUser(what, token, pkg, mCurrentUserId); } + // TODO(b/117478341): make it aware of multi-display if needed. @Override public void disableForUser(int what, IBinder token, String pkg, int userId) { enforceStatusBar(); synchronized (mLock) { - disableLocked(userId, what, token, pkg, 1); + disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1); } } + // TODO(b/117478341): make it aware of multi-display if needed. /** * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags. * To re-enable everything, pass {@link #DISABLE_NONE}. @@ -689,6 +680,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { disable2ForUser(what, token, pkg, mCurrentUserId); } + // TODO(b/117478341): make it aware of multi-display if needed. /** * Disable additional status bar features for a given user. Pass the bitwise-or of the * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}. @@ -700,11 +692,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub { enforceStatusBar(); synchronized (mLock) { - disableLocked(userId, what, token, pkg, 2); + disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2); } } - private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) { + private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg, + int whichFlag) { // It's important that the the callback and the call to mBar get done // in the same order when multiple threads are calling this function // so they are paired correctly. The messages on the handler will be @@ -723,22 +716,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub { disabledData += " ([" + i + "] " + tok + "), "; } disabledData += " }"; - Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1 - + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => " - + disabledData); - } + final UiState state = getUiState(displayId); - if (net1 != mDisabled1 || net2 != mDisabled2) { - mDisabled1 = net1; - mDisabled2 = net2; - mHandler.post(new Runnable() { - public void run() { - mNotificationDelegate.onSetDisabled(net1); - } - }); + Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1 + + ", mDisabled1=" + state.mDisabled1 + ", token=" + token + + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData); + } + final UiState state = getUiState(displayId); + if (state.disableEquals(net1, net2)) { + state.setDisabled(net1, net2); + mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1)); if (mBar != null) { try { - mBar.disable(net1, net2); + mBar.disable(displayId, net1, net2); } catch (RemoteException ex) { } } @@ -808,26 +798,27 @@ public class StatusBarManagerService extends IStatusBarService.Stub { * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}. */ - private void topAppWindowChanged(final boolean menuVisible) { + private void topAppWindowChanged(int displayId, final boolean menuVisible) { enforceStatusBar(); - if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key"); - + if (SPEW) { + Slog.d(TAG, "display#" + displayId + ": " + + (menuVisible ? "showing" : "hiding") + " MENU key"); + } synchronized(mLock) { - mMenuVisible = menuVisible; - mHandler.post(new Runnable() { - public void run() { - if (mBar != null) { - try { - mBar.topAppWindowChanged(menuVisible); - } catch (RemoteException ex) { - } + getUiState(displayId).setMenuVisible(menuVisible); + mHandler.post(() -> { + if (mBar != null) { + try { + mBar.topAppWindowChanged(displayId, menuVisible); + } catch (RemoteException ex) { } } }); } } + // TODO(b/117478341): support back button change when IME is showing on a external display. @Override public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition, final boolean showImeSwitcher) { @@ -841,39 +832,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub { // In case of IME change, we need to call up setImeWindowStatus() regardless of // mImeWindowVis because mImeWindowVis may not have been set to false when the // previous IME was destroyed. - mImeWindowVis = vis; - mImeBackDisposition = backDisposition; - mImeToken = token; - mShowImeSwitcher = showImeSwitcher; - mHandler.post(new Runnable() { - public void run() { - if (mBar != null) { - try { - mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher); - } catch (RemoteException ex) { - } - } - } + // TODO(b/117478341): support back button change when IME is showing on a external + // display. + getUiState(DEFAULT_DISPLAY) + .setImeWindowState(vis, backDisposition, showImeSwitcher, token); + + mHandler.post(() -> { + if (mBar == null) return; + try { + // TODO(b/117478341): support back button change when IME is showing on a + // external display. + mBar.setImeWindowStatus( + DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher); + } catch (RemoteException ex) { } }); } } @Override - public void setSystemUiVisibility(int vis, int mask, String cause) { - setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause); + public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) { + final UiState state = getUiState(displayId); + setSystemUiVisibility(displayId, vis, 0, 0, mask, + state.mFullscreenStackBounds, state.mDockedStackBounds, cause); } - private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, - Rect fullscreenBounds, Rect dockedBounds, String cause) { + private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, + int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) { // also allows calls from window manager which is in this process. enforceStatusBarService(); if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")"); synchronized (mLock) { - updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask, + updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask, fullscreenBounds, dockedBounds); disableLocked( + displayId, mCurrentUserId, vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken, @@ -881,30 +875,107 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } - private void updateUiVisibilityLocked(final int vis, + private void updateUiVisibilityLocked(final int displayId, final int vis, final int fullscreenStackVis, final int dockedStackVis, final int mask, final Rect fullscreenBounds, final Rect dockedBounds) { - if (mSystemUiVisibility != vis - || mFullscreenStackSysUiVisibility != fullscreenStackVis - || mDockedStackSysUiVisibility != dockedStackVis - || !mFullscreenStackBounds.equals(fullscreenBounds) - || !mDockedStackBounds.equals(dockedBounds)) { + final UiState state = getUiState(displayId); + if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis, + fullscreenBounds, dockedBounds)) { + state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds, + dockedBounds); + mHandler.post(() -> { + if (mBar != null) { + try { + mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis, + dockedStackVis, mask, fullscreenBounds, dockedBounds); + } catch (RemoteException ex) { + Log.w(TAG, "Can not get StatusBar!"); + } + } + }); + } + } + + /** + * @return {@link UiState} specified by {@code displayId}. + * + * <p> + * Note: If {@link UiState} specified by {@code displayId} does not exist, {@link UiState} + * will be allocated and {@code mDisplayUiState} will be updated accordingly. + * <p/> + */ + private UiState getUiState(int displayId) { + UiState state = mDisplayUiState.get(displayId); + if (state == null) { + state = new UiState(); + mDisplayUiState.put(displayId, state); + } + return state; + } + + private class UiState { + private int mSystemUiVisibility = 0; + private int mFullscreenStackSysUiVisibility = 0; + private int mDockedStackSysUiVisibility = 0; + private final Rect mFullscreenStackBounds = new Rect(); + private final Rect mDockedStackBounds = new Rect(); + private boolean mMenuVisible = false; + private int mDisabled1 = 0; + private int mDisabled2 = 0; + private int mImeWindowVis = 0; + private int mImeBackDisposition = 0; + private boolean mShowImeSwitcher = false; + private IBinder mImeToken = null; + + private int getDisabled1() { + return mDisabled1; + } + + private int getDisabled2() { + return mDisabled2; + } + + private void setDisabled(int disabled1, int disabled2) { + mDisabled1 = disabled1; + mDisabled2 = disabled2; + } + + private boolean isMenuVisible() { + return mMenuVisible; + } + + private void setMenuVisible(boolean menuVisible) { + mMenuVisible = menuVisible; + } + + private boolean disableEquals(int disabled1, int disabled2) { + return mDisabled1 == disabled1 && mDisabled2 == disabled2; + } + + private void setSystemUiState(final int vis, final int fullscreenStackVis, + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { mSystemUiVisibility = vis; mFullscreenStackSysUiVisibility = fullscreenStackVis; mDockedStackSysUiVisibility = dockedStackVis; mFullscreenStackBounds.set(fullscreenBounds); mDockedStackBounds.set(dockedBounds); - mHandler.post(new Runnable() { - public void run() { - if (mBar != null) { - try { - mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis, - mask, fullscreenBounds, dockedBounds); - } catch (RemoteException ex) { - } - } - } - }); + } + + private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis, + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { + return mSystemUiVisibility == vis + && mFullscreenStackSysUiVisibility == fullscreenStackVis + && mDockedStackSysUiVisibility == dockedStackVis + && mFullscreenStackBounds.equals(fullscreenBounds) + && mDockedStackBounds.equals(dockedBounds); + } + + private void setImeWindowState(final int vis, final int backDisposition, + final boolean showImeSwitcher, final IBinder token) { + mImeWindowVis = vis; + mImeBackDisposition = backDisposition; + mShowImeSwitcher = showImeSwitcher; + mImeToken = token; } } @@ -939,6 +1010,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { // ================================================================================ // Callbacks from the status bar service. // ================================================================================ + // TODO(b/118592525): refactor it as an IStatusBar API. @Override public void registerStatusBar(IStatusBar bar, List<String> iconSlots, List<StatusBarIcon> iconList, int switches[], List<IBinder> binders, @@ -956,18 +1028,21 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } synchronized (mLock) { + // TODO(b/118592525): Currently, status bar only works on the default display. + // Make it aware of multi-display if needed. + final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY); switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1); - switches[1] = mSystemUiVisibility; - switches[2] = mMenuVisible ? 1 : 0; - switches[3] = mImeWindowVis; - switches[4] = mImeBackDisposition; - switches[5] = mShowImeSwitcher ? 1 : 0; + switches[1] = state.mSystemUiVisibility; + switches[2] = state.mMenuVisible ? 1 : 0; + switches[3] = state.mImeWindowVis; + switches[4] = state.mImeBackDisposition; + switches[5] = state.mShowImeSwitcher ? 1 : 0; switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2); - switches[7] = mFullscreenStackSysUiVisibility; - switches[8] = mDockedStackSysUiVisibility; - binders.add(mImeToken); - fullscreenStackBounds.set(mFullscreenStackBounds); - dockedStackBounds.set(mDockedStackBounds); + switches[7] = state.mFullscreenStackSysUiVisibility; + switches[8] = state.mDockedStackSysUiVisibility; + binders.add(state.mImeToken); + fullscreenStackBounds.set(state.mFullscreenStackBounds); + dockedStackBounds.set(state.mDockedStackBounds); } } @@ -1309,8 +1384,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; synchronized (mLock) { - pw.println(" mDisabled1=0x" + Integer.toHexString(mDisabled1)); - pw.println(" mDisabled2=0x" + Integer.toHexString(mDisabled2)); + for (int i = 0; i < mDisplayUiState.size(); i++) { + final int key = mDisplayUiState.keyAt(i); + final UiState state = mDisplayUiState.get(key); + pw.println(" displayId=" + key); + pw.println(" mDisabled1=0x" + Integer.toHexString(state.getDisabled1())); + pw.println(" mDisabled2=0x" + Integer.toHexString(state.getDisabled2())); + } final int N = mDisableRecords.size(); pw.println(" mDisableRecords.size=" + N); for (int i=0; i<N; i++) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index cfec8effeede..64ff9cf21ba0 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2822,8 +2822,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub IoUtils.closeQuietly(stream); if (!success) { - wpdData.mWidth = -1; - wpdData.mHeight = -1; wallpaper.cropHint.set(0, 0, 0, 0); wpdData.mPadding.set(0, 0, 0, 0); wallpaper.name = ""; @@ -2839,6 +2837,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY); ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); if (lockWallpaper != null) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 255a003bb542..daf4b8ba6d48 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1640,7 +1640,6 @@ final class ActivityRecord extends ConfigurationContainer { final IAppTransitionAnimationSpecsFuture specsFuture = pendingOptions.getSpecsFuture(); if (specsFuture != null) { - // TODO(multidisplay): Shouldn't be really used anymore from next CL. displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( specsFuture, pendingOptions.getOnAnimationStartListener(), animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); @@ -1669,7 +1668,6 @@ final class ActivityRecord extends ConfigurationContainer { .overridePendingAppTransitionStartCrossProfileApps(); break; case ANIM_REMOTE_ANIMATION: - // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL. displayContent.mAppTransition.overridePendingAppTransitionRemote( pendingOptions.getRemoteAnimationAdapter()); break; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 0cdbedba7318..0fc890a39fed 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -402,7 +402,8 @@ public abstract class ActivityTaskManagerInternal { int wakefulness); /** Writes the current window process states to the proto stream. */ - public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage); + public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage, + int wakeFullness, boolean testPssMode); /** Dump the current activities state. */ public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0967afda6d2d..61eb9d4b8abf 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -863,6 +863,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ Configuration getGlobalConfigurationForCallingPid() { final int pid = Binder.getCallingPid(); + return getGlobalConfigurationForPid(pid); + } + + /** + * Return the global configuration used by the process corresponding to the given pid. + */ + Configuration getGlobalConfigurationForPid(int pid) { if (pid == MY_PID || pid < 0) { return getGlobalConfiguration(); } @@ -4701,26 +4708,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - void writeSleepStateToProto(ProtoOutputStream proto) { + private void writeSleepStateToProto(ProtoOutputStream proto, int wakeFullness, + boolean testPssMode) { + final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS); + proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS, + PowerManagerInternal.wakefulnessToProtoEnum(wakeFullness)); for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) { proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString()); } - - if (mRunningVoice != null) { - final long vrToken = proto.start( - ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE); - proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, - mRunningVoice.toString()); - mVoiceWakeLock.writeToProto( - proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK); - proto.end(vrToken); - } - proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping); proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown); - mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER); + proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, + testPssMode); + proto.end(sleepToken); } int getCurrentUserId() { @@ -6599,12 +6601,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) { + public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage, + int wakeFullness, boolean testPssMode) { synchronized (mGlobalLock) { if (dumpPackage == null) { getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION); proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange); - writeSleepStateToProto(proto); + writeSleepStateToProto(proto, wakeFullness, testPssMode); + if (mRunningVoice != null) { + final long vrToken = proto.start( + ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE); + proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, + mRunningVoice.toString()); + mVoiceWakeLock.writeToProto( + proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK); + proto.end(vrToken); + } + mVrController.writeToProto(proto, + ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER); if (mController != null) { final long token = proto.start(CONTROLLER); proto.write(CONTROLLER, mController.toString()); diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 64553a8c7ade..e779d1bb20f5 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -319,6 +319,13 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { onOverrideConfigurationChanged(mTmpConfig); } + /** Sets the windowing mode for the configuration container. */ + void setDisplayWindowingMode(int windowingMode) { + mTmpConfig.setTo(getOverrideConfiguration()); + mTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode); + onOverrideConfigurationChanged(mTmpConfig); + } + /** * Returns true if this container is currently in multi-window mode. I.e. sharing the screen * with another activity. diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 05e82676a40a..aba2eb38ce15 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1546,6 +1546,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int dh = displayInfo.logicalHeight; config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; config.windowConfiguration.setWindowingMode(getWindowingMode()); + config.windowConfiguration.setDisplayWindowingMode(getWindowingMode()); config.windowConfiguration.setRotation(displayInfo.rotation); final float density = mDisplayMetrics.density; @@ -1953,6 +1954,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWmService.mWindowsChanged = true; } + @Override + public void setWindowingMode(int windowingMode) { + super.setWindowingMode(windowingMode); + super.setDisplayWindowingMode(windowingMode); + } + + @Override + void setDisplayWindowingMode(int windowingMode) { + setWindowingMode(windowingMode); + } + /** * In split-screen mode we process the IME containers above the docked divider * rather than directly above their target. diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 581cec928334..d4bd91b008d4 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -222,6 +222,7 @@ public class DisplayPolicy { private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; private volatile boolean mHdmiPlugged; + private volatile boolean mHasStatusBar; private volatile boolean mHasNavigationBar; // Can the navigation bar ever move to the side? private volatile boolean mNavigationBarCanMove; @@ -523,6 +524,7 @@ public class DisplayPolicy { mNavigationBarCanMove = width != height && shortSizeDp < 600; if (mDisplayContent.isDefaultDisplay) { + mHasStatusBar = true; mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar); // Allow a system property to override this. Used by the emulator. @@ -534,6 +536,7 @@ public class DisplayPolicy { mHasNavigationBar = true; } } else { + mHasStatusBar = false; mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations(); } } @@ -589,6 +592,10 @@ public class DisplayPolicy { return mHasNavigationBar; } + public boolean hasStatusBar() { + return mHasStatusBar; + } + public boolean navigationBarCanMove() { return mNavigationBarCanMove; } @@ -2493,12 +2500,19 @@ public class DisplayPolicy { final int landscapeRotation = displayRotation.getLandscapeRotation(); final int seascapeRotation = displayRotation.getSeascapeRotation(); - mStatusBarHeightForRotation[portraitRotation] = - mStatusBarHeightForRotation[upsideDownRotation] = - res.getDimensionPixelSize(R.dimen.status_bar_height_portrait); - mStatusBarHeightForRotation[landscapeRotation] = - mStatusBarHeightForRotation[seascapeRotation] = - res.getDimensionPixelSize(R.dimen.status_bar_height_landscape); + if (hasStatusBar()) { + mStatusBarHeightForRotation[portraitRotation] = + mStatusBarHeightForRotation[upsideDownRotation] = + res.getDimensionPixelSize(R.dimen.status_bar_height_portrait); + mStatusBarHeightForRotation[landscapeRotation] = + mStatusBarHeightForRotation[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.status_bar_height_landscape); + } else { + mStatusBarHeightForRotation[portraitRotation] = + mStatusBarHeightForRotation[upsideDownRotation] = + mStatusBarHeightForRotation[landscapeRotation] = + mStatusBarHeightForRotation[seascapeRotation] = 0; + } // Height of the navigation bar when presented horizontally at bottom mNavigationBarHeightForRotationDefault[portraitRotation] = diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 88b22cb5e01e..c1e9a7353fd5 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -194,7 +194,7 @@ final class InputMonitor { final boolean hasFocus, final boolean hasWallpaper) { // Add a window to our list of input windows. inputWindowHandle.name = child.toString(); - flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags); + flags = child.getSurfaceTouchableRegion(inputWindowHandle.touchableRegion, flags); inputWindowHandle.layoutParamsFlags = flags; inputWindowHandle.layoutParamsType = type; inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 58b7e02b3d12..83ba384c9069 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -504,10 +504,12 @@ public class RecentsAnimationController implements DeathRecipient { public void binderDied() { cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); - // Clear associated input consumers on runner death - final InputMonitor inputMonitor = - mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); - inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + synchronized (mService.getWindowManagerLock()) { + // Clear associated input consumers on runner death + final InputMonitor inputMonitor = + mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); + inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + } } void checkAnimationReady(WallpaperController wallpaperController) { diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 4ae2a79e2697..1fb7979fa3e2 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -44,6 +44,7 @@ import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Build; import android.util.Slog; @@ -526,12 +527,24 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { adjustBoundsToAvoidConflict(display, inOutBounds); } + private int convertOrientationToScreenOrientation(int orientation) { + switch (orientation) { + case Configuration.ORIENTATION_LANDSCAPE: + return SCREEN_ORIENTATION_LANDSCAPE; + case Configuration.ORIENTATION_PORTRAIT: + return SCREEN_ORIENTATION_PORTRAIT; + default: + return SCREEN_ORIENTATION_UNSPECIFIED; + } + } + private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display, @NonNull Rect bounds) { int orientation = resolveOrientation(root); if (orientation == SCREEN_ORIENTATION_LOCKED) { - orientation = bounds.isEmpty() ? display.getConfiguration().orientation + orientation = bounds.isEmpty() + ? convertOrientationToScreenOrientation(display.getConfiguration().orientation) : orientationFromBounds(bounds); if (DEBUG) { appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index e83b8634925e..9f1a58770611 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -19,7 +19,6 @@ package com.android.server.wm; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; -import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; @@ -449,11 +448,4 @@ public abstract class WindowManagerInternal { * Return the display Id for given window. */ public abstract int getDisplayIdForWindow(IBinder windowToken); - - // TODO: use WindowProcessController once go/wm-unified is done. - /** - * Notifies the window manager that configuration of the process associated with the input pid - * changed. - */ - public abstract void onProcessConfigurationChanged(int pid, Configuration newConfig); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 52b24b3e7307..002d6d409abe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -735,6 +735,7 @@ public class WindowManagerService extends IWindowManager.Stub final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; + final ActivityTaskManagerService mAtmService; // Indicates whether this device supports wide color gamut / HDR rendering private boolean mHasWideColorGamutSupport; @@ -897,11 +898,10 @@ public class WindowManagerService extends IWindowManager.Stub public static WindowManagerService main(final Context context, final InputManagerService im, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, - final WindowManagerGlobalLock globalLock) { + ActivityTaskManagerService atm) { DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, - globalLock), - 0); + atm), 0); return sInstance; } @@ -923,9 +923,10 @@ public class WindowManagerService extends IWindowManager.Stub private WindowManagerService(Context context, InputManagerService inputManager, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, - WindowManagerGlobalLock globalLock) { + ActivityTaskManagerService atm) { installLock(this, INDEX_WINDOW); - mGlobalLock = globalLock; + mGlobalLock = atm.getGlobalLock(); + mAtmService = atm; mContext = context; mAllowBootMessages = showBootMsgs; mOnlyCore = onlyCore; @@ -7281,19 +7282,6 @@ public class WindowManagerService extends IWindowManager.Stub return Display.INVALID_DISPLAY; } } - - @Override - public void onProcessConfigurationChanged(int pid, Configuration newConfig) { - synchronized (mGlobalLock) { - Configuration currentConfig = mProcessConfigurations.get(pid); - if (currentConfig == null) { - currentConfig = new Configuration(newConfig); - } else { - currentConfig.setTo(newConfig); - } - mProcessConfigurations.put(pid, currentConfig); - } - } } void registerAppFreezeListener(AppFreezeListener listener) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ad43e74eca2f..e115fed4db9a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2143,7 +2143,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - int getTouchableRegion(Region region, int flags) { + int getSurfaceTouchableRegion(Region region, int flags) { final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; if (modal && mAppToken != null) { // Limit the outer touch to the activity stack region. @@ -2171,12 +2171,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); } else { // Not modal or full screen modal - getTouchableRegion(region); + getTouchableRegion(region, true /* forSurface */); } - // The area containing the shadows is not touchable. - region.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top); - return flags; } @@ -2266,8 +2263,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // For child windows we want to use the pid for the parent window in case the the child // window was added from another process. final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid; - mTempConfiguration.setTo(mWmService.mProcessConfigurations.get( - pid, mWmService.mRoot.getConfiguration())); + final Configuration processConfig = + mWmService.mAtmService.getGlobalConfigurationForPid(pid); + mTempConfiguration.setTo(processConfig == null + ? mWmService.mRoot.getConfiguration() : processConfig); return mTempConfiguration; } @@ -2808,7 +2807,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP frame.right - inset.right, frame.bottom - inset.bottom); } + /** Get the touchable region in global coordinates. */ void getTouchableRegion(Region outRegion) { + getTouchableRegion(outRegion, false /* forSurface */); + } + + /** If {@param forSuface} is {@code true}, the region will be translated to surface based. */ + private void getTouchableRegion(Region outRegion, boolean forSurface) { if (inPinnedWindowingMode() && !isFocused()) { outRegion.setEmpty(); return; @@ -2819,22 +2824,26 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP default: case TOUCHABLE_INSETS_FRAME: outRegion.set(frame); - outRegion.translate(-frame.left, -frame.top); break; case TOUCHABLE_INSETS_CONTENT: applyInsets(outRegion, frame, mGivenContentInsets); - outRegion.translate(-frame.left, -frame.top); break; case TOUCHABLE_INSETS_VISIBLE: applyInsets(outRegion, frame, mGivenVisibleInsets); - outRegion.translate(-frame.left, -frame.top); break; case TOUCHABLE_INSETS_REGION: { outRegion.set(mGivenTouchableRegion); break; } } - outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top); + + if (forSurface) { + if (mTouchableInsets != TOUCHABLE_INSETS_REGION) { + outRegion.translate(-frame.left, -frame.top); + } + outRegion.getBounds(mTmpRect); + applyInsets(outRegion, mTmpRect, mAttrs.surfaceInsets); + } } private void cropRegionToStackBoundsIfNeeded(Region region) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index b9dabb9ee7ec..65d32450b27f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -120,4 +120,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { int userHandle) { return false; } + + @Override + public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) { + return Collections.emptyList(); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6fbb850e4e40..f68f4d7424f4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -13426,10 +13426,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceCrossUsersPermission(userHandle); synchronized (getLockObject()) { final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); - if (admin != null && admin.mCrossProfileCalendarPackages != null) { + if (admin != null) { return admin.mCrossProfileCalendarPackages.contains(packageName); } } return false; } + + @Override + public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) { + if (!mHasFeature) { + return Collections.emptyList(); + } + enforceCrossUsersPermission(userHandle); + synchronized (getLockObject()) { + final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); + if (admin != null) { + return new ArrayList<String>(admin.mCrossProfileCalendarPackages); + } + } + return Collections.emptyList(); + } } diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java index 14912c474995..05b8201f112e 100644 --- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java +++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java @@ -16,6 +16,7 @@ package com.android.server.intelligence; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.os.IBinder; @@ -25,12 +26,13 @@ import android.service.intelligence.SmartSuggestionsService; import android.service.intelligence.SnapshotData; import android.util.Slog; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.server.AbstractRemoteService; +import com.android.server.infra.AbstractRemoteService; import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks; @@ -98,8 +100,10 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks * Requests the service to autofill the given field. */ public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client, - int autofillSessionId, @NonNull AutofillId focusedId) { - mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId); + int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue) { + mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId, + focusedValue); if (mAutofillCallback == null) { mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId); } diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java index 4c68064b46f6..a760cbd039e4 100644 --- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java +++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java @@ -35,6 +35,7 @@ import android.os.UserManager; import android.service.intelligence.InteractionSessionId; import android.util.Slog; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import android.view.intelligence.IIntelligenceManager; @@ -43,8 +44,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; -import com.android.server.AbstractMasterSystemService; import com.android.server.LocalServices; +import com.android.server.infra.AbstractMasterSystemService; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -65,6 +66,8 @@ public final class IntelligenceManagerService extends static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions"; + private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + @GuardedBy("mLock") private ActivityManagerInternal mAm; @@ -75,12 +78,6 @@ public final class IntelligenceManagerService extends } @Override // from AbstractMasterSystemService - protected String getServiceSettingsProperty() { - // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd - return "smart_suggestions_service"; - } - - @Override // from AbstractMasterSystemService protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { return new IntelligencePerUserService(this, mLock, resolvedUserId); @@ -104,6 +101,11 @@ public final class IntelligenceManagerService extends getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG); } + @Override // from AbstractMasterSystemService + protected int getMaximumTemporaryServiceDurationMs() { + return MAX_TEMP_SERVICE_DURATION_MS; + } + // Called by Shell command. void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) { Slog.i(TAG, "destroySessions() for userId " + userId); @@ -256,12 +258,13 @@ public final class IntelligenceManagerService extends @Override public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId, @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken, - int autofillSessionId, @NonNull AutofillId focusedId) { + int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue) { synchronized (mLock) { final IntelligencePerUserService service = peekServiceForUserLocked(userId); if (service != null) { return service.requestAutofill(client, activityToken, autofillSessionId, - focusedId); + focusedId, focusedValue); } } return null; diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java index dbf8601849bb..c8448e142902 100644 --- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java +++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java @@ -36,16 +36,18 @@ import android.os.IBinder; import android.os.RemoteException; import android.service.intelligence.InteractionSessionId; import android.service.intelligence.SnapshotData; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import android.view.intelligence.ContentCaptureManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; -import com.android.server.AbstractPerUserSystemService; +import com.android.server.infra.AbstractPerUserSystemService; import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; import java.io.PrintWriter; @@ -77,17 +79,24 @@ final class IntelligencePerUserService protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent) throws NameNotFoundException { + int flags = PackageManager.GET_META_DATA; + final boolean isTemp = isTemporaryServiceSetLocked(); + if (!isTemp) { + flags |= PackageManager.MATCH_SYSTEM_ONLY; + } + ServiceInfo si; try { - // TODO(b/111276913): must check that either the service is from a system component, - // or it matches a service set by shell cmd (so it can be used on CTS tests and when - // OEMs are implementing the real service - si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, - PackageManager.GET_META_DATA, mUserId); + si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId); } catch (RemoteException e) { Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e); return null; } + if (si == null) { + Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)") + + " " + serviceComponent.flattenToShortString()); + return null; + } if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) { Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName + "' does not require permission " @@ -105,6 +114,13 @@ final class IntelligencePerUserService return super.updateLocked(disabled); } + @Override // from PerUserSystemService + protected String getDefaultComponentName() { + final String name = getContext() + .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService); + return TextUtils.isEmpty(name) ? null : name; + } + // TODO(b/111276913): log metrics @GuardedBy("mLock") public void startSessionLocked(@NonNull IBinder activityToken, @@ -274,13 +290,15 @@ final class IntelligencePerUserService } public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client, - @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) { + @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue) { synchronized (mLock) { final ContentCaptureSession session = getSession(activityToken); if (session != null) { // TODO(b/111330312): log metrics if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()"); - return session.requestAutofillLocked(client, autofillSessionId, focusedId); + return session.requestAutofillLocked(client, autofillSessionId, focusedId, + focusedValue); } if (mMaster.debug) { Slog.d(TAG, "requestAutofill(): no session for " + activityToken); diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java index b7c1f789e46c..0d92a972aa96 100644 --- a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java +++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java @@ -75,6 +75,10 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { pw.println(" set bind-instant-service-allowed [true | false]"); pw.println(" Sets whether binding to services provided by instant apps is allowed"); pw.println(""); + pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]"); + pw.println(" Temporarily (for DURATION ms) changes the service implemtation."); + pw.println(" To reset, call with just the USER_ID argument."); + pw.println(""); pw.println(" list sessions [--user USER_ID]"); pw.println(" Lists all pending sessions."); pw.println(""); @@ -101,6 +105,8 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { switch(what) { case "bind-instant-service-allowed": return setBindInstantService(pw); + case "temporary-service": + return setTemporaryService(); default: pw.println("Invalid set: " + what); return -1; @@ -131,6 +137,18 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { } } + private int setTemporaryService() { + final int userId = getNextIntArgRequired(); + final String serviceName = getNextArg(); + if (serviceName == null) { + mService.resetTemporaryService(userId); + return 0; + } + final int duration = getNextIntArgRequired(); + mService.setTemporaryService(userId, serviceName, duration); + return 0; + } + private int requestDestroy(PrintWriter pw) { if (!isNextArgSessions(pw)) { return -1; @@ -204,4 +222,8 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { } return UserHandle.USER_ALL; } + + private int getNextIntArgRequired() { + return Integer.parseInt(getNextArgRequired()); + } } diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java index d9f4f20dc971..c4fbdca0ff2f 100644 --- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java +++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; +import android.os.SystemClock; import android.service.intelligence.ContentCaptureEventsRequest; import android.service.intelligence.IIntelligenceService; import android.service.intelligence.InteractionContext; @@ -32,11 +33,12 @@ import android.text.format.DateUtils; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; +import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import com.android.internal.os.IResultReceiver; -import com.android.server.AbstractMultiplePendingRequestsRemoteService; +import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService; import java.util.List; @@ -114,10 +116,10 @@ final class RemoteIntelligenceService */ public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client, int autofillSessionId, - @NonNull AutofillId focusedId) { + @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) { cancelScheduledUnbind(); scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId, - focusedId)); + focusedId, focusedValue)); } /** @@ -222,16 +224,20 @@ final class RemoteIntelligenceService private static final class PendingAutofillRequest extends MyPendingRequest { private final @NonNull AutofillId mFocusedId; + private final @Nullable AutofillValue mFocusedValue; private final @NonNull IAutoFillManagerClient mClient; private final int mAutofillSessionId; + private final long mRequestTime = SystemClock.elapsedRealtime(); protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service, @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client, - int autofillSessionId, @NonNull AutofillId focusedId) { + int autofillSessionId, @NonNull AutofillId focusedId, + @Nullable AutofillValue focusedValue) { super(service, sessionId); mClient = client; mAutofillSessionId = autofillSessionId; mFocusedId = focusedId; + mFocusedValue = focusedValue; } @Override // from MyPendingRequest @@ -243,7 +249,7 @@ final class RemoteIntelligenceService final IBinder realClient = resultData .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); remoteService.mService.onAutofillRequest(mSessionId, realClient, - mAutofillSessionId, mFocusedId); + mAutofillSessionId, mFocusedId, mFocusedValue, mRequestTime); } }; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9c6f4b356a0b..c4d2a914facf 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -22,6 +22,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.NonNull; import android.app.ActivityThread; import android.app.INotificationManager; import android.app.usage.UsageStatsManagerInternal; @@ -55,7 +56,9 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.storage.IStorageManager; +import android.provider.Settings; import android.sysprop.VoldProperties; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; @@ -121,6 +124,7 @@ import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.role.RoleManagerService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; +import com.android.server.signedconfig.SignedConfigService; import com.android.server.soundtrigger.SoundTriggerService; import com.android.server.stats.StatsCompanionService; import com.android.server.statusbar.StatusBarManagerService; @@ -797,10 +801,6 @@ public final class SystemServer { boolean disableSystemTextClassifier = SystemProperties.getBoolean( "config.disable_systemtextclassifier", false); - //TODO(b/111276913): temporarily disabled until the manager is properly implemented to - // ignore events when disabled and buffer when enabled - boolean disableIntelligence = SystemProperties.getBoolean( - "config.disable_intelligence", true); boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", @@ -926,7 +926,7 @@ public final class SystemServer { ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE); mSensorServiceStart = null; wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, - new PhoneWindowManager(), mWindowManagerGlobalLock); + new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); ServiceManager.addService(Context.INPUT_SERVICE, inputManager, @@ -995,6 +995,11 @@ public final class SystemServer { traceBeginAndSlog("PinnerService"); mSystemServiceManager.startService(PinnerService.class); traceEnd(); + + traceBeginAndSlog("SignedConfigService"); + SignedConfigService.registerUpdateReceiver(mSystemContext); + traceEnd(); + } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); @@ -1131,13 +1136,7 @@ public final class SystemServer { traceEnd(); } - if (!disableIntelligence) { - traceBeginAndSlog("StartIntelligenceService"); - mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS); - traceEnd(); - } else { - Slog.d(TAG, "IntelligenceService disabled"); - } + startIntelligenceService(context); // NOTE: ClipboardService indirectly depends on IntelligenceService traceBeginAndSlog("StartClipboardService"); @@ -2101,6 +2100,37 @@ public final class SystemServer { }, BOOT_TIMINGS_TRACE_LOG); } + private void startIntelligenceService(@NonNull Context context) { + + // First check if it was explicitly enabled by Settings + boolean explicitlySupported = false; + final String settings = Settings.Global.getString(context.getContentResolver(), + Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED); + if (settings != null) { + explicitlySupported = Boolean.parseBoolean(settings); + if (explicitlySupported) { + Slog.d(TAG, "IntelligenceService explicitly enabled by Settings"); + } else { + Slog.d(TAG, "IntelligenceService explicitly disabled by Settings"); + return; + } + } + + // Then check if OEM overlaid the resource that defines the service. + if (!explicitlySupported) { + final String serviceName = context + .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService); + if (TextUtils.isEmpty(serviceName)) { + Slog.d(TAG, "IntelligenceService disabled because config resource is not overlaid"); + return; + } + } + + traceBeginAndSlog("StartIntelligenceService"); + mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS); + traceEnd(); + } + static final void startSystemUi(Context context, WindowManagerService windowManager) { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.systemui", diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java new file mode 100644 index 000000000000..5342efa18a97 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java @@ -0,0 +1,148 @@ +/* + * 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.backup.encryption.keys; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; +import android.security.keystore.recovery.InternalRecoveryServiceException; +import android.security.keystore.recovery.RecoveryController; + +import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException; +import com.android.server.testing.shadows.ShadowRecoveryController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.security.SecureRandom; +import java.util.Optional; + +/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class}) +public class RecoverableKeyStoreSecondaryKeyManagerTest { + private static final String BACKUP_KEY_ALIAS_PREFIX = + "com.android.server.backup/recoverablekeystore/"; + private static final int BITS_PER_BYTE = 8; + private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE; + private static final int HEX_PER_BYTE = 2; + private static final int BACKUP_KEY_ALIAS_LENGTH = + BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE; + private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS"; + + private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager; + private Context mContext; + + /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */ + @Before + public void setUp() throws Exception { + mContext = RuntimeEnvironment.application; + + mRecoverableKeyStoreSecondaryKeyManager = + new RecoverableKeyStoreSecondaryKeyManager( + RecoveryController.getInstance(mContext), new SecureRandom()); + } + + /** Reset the {@link ShadowRecoveryController}. */ + @After + public void tearDown() throws Exception { + ShadowRecoveryController.reset(); + } + + /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */ + @Test + public void generate_generatesKeyWithExpectedPrefix() throws Exception { + RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); + + assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX); + } + + /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */ + @Test + public void generate_generatesKeyWithExpectedLength() throws Exception { + RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); + + assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH); + } + + /** Ensure that hidden API exceptions are rethrown when generating keys. */ + @Test + public void generate_encounteringHiddenApiException_rethrowsException() { + ShadowRecoveryController.setThrowsInternalError(true); + + assertThrows( + InternalRecoveryServiceException.class, + mRecoverableKeyStoreSecondaryKeyManager::generate); + } + + /** Ensure that retrieved keys correspond to those generated earlier. */ + @Test + public void get_getsKeyGeneratedByController() throws Exception { + RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); + + Optional<RecoverableKeyStoreSecondaryKey> retrievedKey = + mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias()); + + assertThat(retrievedKey.isPresent()).isTrue(); + assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias()); + assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey()); + } + + /** + * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} + * for nonexistent aliases returns an emtpy {@link Optional}. + */ + @Test + public void get_forNonExistentKey_returnsEmptyOptional() throws Exception { + Optional<RecoverableKeyStoreSecondaryKey> retrievedKey = + mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS); + + assertThat(retrievedKey.isPresent()).isFalse(); + } + + /** + * Ensure that exceptions occurring during {@link + * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown. + */ + @Test + public void get_encounteringInternalException_doesNotPropagateException() throws Exception { + ShadowRecoveryController.setThrowsInternalError(true); + + // Should not throw exception + mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS); + } + + /** Ensure that keys are correctly removed from the store. */ + @Test + public void remove_removesKeyFromRecoverableStore() throws Exception { + RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); + + mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias()); + + assertThat(RecoveryController.getInstance(mContext).getAliases()) + .doesNotContain(key.getAlias()); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java new file mode 100644 index 000000000000..89977f82c145 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java @@ -0,0 +1,163 @@ +/* + * 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.backup.encryption.keys; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; +import android.security.keystore.recovery.RecoveryController; + +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status; +import com.android.server.backup.testing.CryptoTestUtils; +import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException; +import com.android.server.testing.shadows.ShadowRecoveryController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import javax.crypto.SecretKey; + +/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class}) +public class RecoverableKeyStoreSecondaryKeyTest { + private static final String TEST_ALIAS = "test"; + private static final int NONEXISTENT_STATUS_CODE = 42; + + private RecoverableKeyStoreSecondaryKey mSecondaryKey; + private SecretKey mGeneratedSecretKey; + private Context mContext; + + /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */ + @Before + public void setUp() throws Exception { + mContext = RuntimeEnvironment.application; + mGeneratedSecretKey = CryptoTestUtils.generateAesKey(); + mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey); + } + + /** Reset the {@link ShadowRecoveryController}. */ + @After + public void tearDown() throws Exception { + ShadowRecoveryController.reset(); + } + + /** + * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in + * the constructor. + */ + @Test + public void getAlias() { + String alias = mSecondaryKey.getAlias(); + + assertThat(alias).isEqualTo(TEST_ALIAS); + } + + /** + * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied + * in the constructor. + */ + @Test + public void getSecretKey() { + SecretKey secretKey = mSecondaryKey.getSecretKey(); + + assertThat(secretKey).isEqualTo(mGeneratedSecretKey); + } + + /** + * Checks that passing a secret key that is null to the constructor throws an exception. + */ + @Test + public void constructor_withNullSecretKey_throwsNullPointerException() { + assertThrows( + NullPointerException.class, + () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null)); + } + + /** + * Checks that passing an alias that is null to the constructor throws an exception. + */ + @Test + public void constructor_withNullAlias_throwsNullPointerException() { + assertThrows( + NullPointerException.class, + () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey)); + } + + /** Checks that the synced status is returned correctly. */ + @Test + public void getStatus_whenSynced_returnsSynced() throws Exception { + setStatus(RecoveryController.RECOVERY_STATUS_SYNCED); + + int status = mSecondaryKey.getStatus(mContext); + + assertThat(status).isEqualTo(Status.SYNCED); + } + + /** Checks that the in progress sync status is returned correctly. */ + @Test + public void getStatus_whenNotSynced_returnsNotSynced() throws Exception { + setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); + + int status = mSecondaryKey.getStatus(mContext); + + assertThat(status).isEqualTo(Status.NOT_SYNCED); + } + + /** Checks that the failure status is returned correctly. */ + @Test + public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception { + setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); + + int status = mSecondaryKey.getStatus(mContext); + + assertThat(status).isEqualTo(Status.DESTROYED); + } + + /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */ + @Test + public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception { + setStatus(NONEXISTENT_STATUS_CODE); + + int status = mSecondaryKey.getStatus(mContext); + + assertThat(status).isEqualTo(Status.NOT_SYNCED); + } + + /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */ + @Test + public void getStatus_onInternalError_returnsNotSynced() throws Exception { + ShadowRecoveryController.setThrowsInternalError(true); + + int status = mSecondaryKey.getStatus(mContext); + + assertThat(status).isEqualTo(Status.NOT_SYNCED); + } + + private void setStatus(int status) throws Exception { + ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java new file mode 100644 index 000000000000..48216f8d7aca --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java @@ -0,0 +1,73 @@ +/* + * 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.backup.encryption.keys; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.security.SecureRandom; + +import javax.crypto.SecretKey; + +/** Tests for {@link TertiaryKeyGenerator}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class TertiaryKeyGeneratorTest { + private static final String KEY_ALGORITHM = "AES"; + private static final int KEY_SIZE_BITS = 256; + + private TertiaryKeyGenerator mTertiaryKeyGenerator; + + /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */ + @Before + public void setUp() { + mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom()); + } + + /** Generated keys should be AES keys. */ + @Test + public void generate_generatesAESKeys() { + SecretKey secretKey = mTertiaryKeyGenerator.generate(); + + assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM); + } + + /** Generated keys should be 256 bits in size. */ + @Test + public void generate_generates256BitKeys() { + SecretKey secretKey = mTertiaryKeyGenerator.generate(); + + assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8); + } + + /** + * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys. + */ + @Test + public void generate_generatesNewKeys() { + SecretKey key1 = mTertiaryKeyGenerator.generate(); + SecretKey key2 = mTertiaryKeyGenerator.generate(); + + assertThat(key1).isNotEqualTo(key2); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java new file mode 100644 index 000000000000..49bb410ceb65 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java @@ -0,0 +1,137 @@ +/* + * 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.backup.encryption.keys; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** Tests for {@link TertiaryKeyRotationTracker}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class TertiaryKeyRotationTrackerTest { + private static final String PACKAGE_1 = "com.package.one"; + private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31; + + private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker; + + /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */ + @Before + public void setUp() { + mTertiaryKeyRotationTracker = newInstance(); + } + + /** New packages should not be due for key rotation. */ + @Test + public void isKeyRotationDue_forNewPackage_isFalse() { + // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't + // been seen by mTertiaryKeyRotationTracker before. + boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); + + assertThat(keyRotationDue).isFalse(); + } + + /** + * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} + * backups. + */ + @Test + public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() { + simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1); + + boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); + + assertThat(keyRotationDue).isFalse(); + } + + /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */ + @Test + public void isKeyRotationDue_afterRotationAmountBackups_isTrue() { + simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); + + boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); + + assertThat(keyRotationDue).isTrue(); + } + + /** + * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key + * rotation is due. + */ + @Test + public void resetCountdown_makesKeyRotationNotDue() { + simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); + + mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1); + + assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse(); + } + + /** + * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of + * backups from disk. + */ + @Test + public void isKeyRotationDue_forNewInstance_readsStateFromDisk() { + simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); + + boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1); + + assertThat(keyRotationDueForNewInstance).isTrue(); + } + + /** + * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously + * seen packages for rotation. + */ + @Test + public void markAllForRotation_marksSeenPackagesForKeyRotation() { + simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1); + + mTertiaryKeyRotationTracker.markAllForRotation(); + + assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue(); + } + + /** + * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new + * packages for rotation. + */ + @Test + public void markAllForRotation_doesNotMarkUnseenPackages() { + mTertiaryKeyRotationTracker.markAllForRotation(); + + assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse(); + } + + private void simulateBackups(String packageName, int numberOfBackups) { + while (numberOfBackups > 0) { + mTertiaryKeyRotationTracker.recordBackup(packageName); + numberOfBackups--; + } + } + + private static TertiaryKeyRotationTracker newInstance() { + return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application); + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java new file mode 100644 index 000000000000..9c06d81ce550 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import android.security.keystore.recovery.InternalRecoveryServiceException; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +/** Shadow {@link InternalRecoveryServiceException}. */ +@Implements(InternalRecoveryServiceException.class) +public class ShadowInternalRecoveryServiceException { + private String mMessage; + + @Implementation + public void __constructor__(String message) { + mMessage = message; + } + + @Implementation + public void __constructor__(String message, Throwable cause) { + mMessage = message; + } + + @Implementation + public String getMessage() { + return mMessage; + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java new file mode 100644 index 000000000000..7dad8a4e3ff3 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import android.content.Context; +import android.security.keystore.recovery.InternalRecoveryServiceException; +import android.security.keystore.recovery.LockScreenRequiredException; +import android.security.keystore.recovery.RecoveryController; + +import com.google.common.collect.ImmutableList; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.lang.reflect.Constructor; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.util.HashMap; +import java.util.List; + +import javax.crypto.KeyGenerator; + +/** + * Shadow of {@link RecoveryController}. + * + * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in + * memory. + */ +@Implements(RecoveryController.class) +public class ShadowRecoveryController { + private static final String KEY_GENERATOR_ALGORITHM = "AES"; + private static final int KEY_SIZE_BITS = 256; + + private static boolean sIsSupported = true; + private static boolean sThrowsInternalError = false; + private static HashMap<String, Key> sKeysByAlias = new HashMap<>(); + private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>(); + + @Implementation + public void __constructor__() { + // do not throw + } + + @Implementation + public static RecoveryController getInstance(Context context) { + // Call non-public constructor. + try { + Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor(); + return constructor.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Implementation + public static boolean isRecoverableKeyStoreEnabled(Context context) { + return sIsSupported; + } + + @Implementation + public Key generateKey(String alias) + throws InternalRecoveryServiceException, LockScreenRequiredException { + maybeThrowError(); + KeyGenerator keyGenerator; + try { + keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + // Should never happen + throw new RuntimeException(e); + } + + keyGenerator.init(KEY_SIZE_BITS); + Key key = keyGenerator.generateKey(); + sKeysByAlias.put(alias, key); + sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); + return key; + } + + @Implementation + public Key getKey(String alias) + throws InternalRecoveryServiceException, UnrecoverableKeyException { + return sKeysByAlias.get(alias); + } + + @Implementation + public void removeKey(String alias) throws InternalRecoveryServiceException { + sKeyStatusesByAlias.remove(alias); + sKeysByAlias.remove(alias); + } + + @Implementation + public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException { + maybeThrowError(); + return sKeyStatusesByAlias.getOrDefault( + alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); + } + + @Implementation + public List<String> getAliases() throws InternalRecoveryServiceException { + return ImmutableList.copyOf(sKeyStatusesByAlias.keySet()); + } + + private static void maybeThrowError() throws InternalRecoveryServiceException { + if (sThrowsInternalError) { + throw new InternalRecoveryServiceException("test error"); + } + } + + /** Sets the recovery status of the key with {@code alias} to {@code status}. */ + public static void setRecoveryStatus(String alias, int status) { + sKeyStatusesByAlias.put(alias, status); + } + + /** Sets all existing keys to being synced. */ + public static void syncAllKeys() { + for (String alias : sKeysByAlias.keySet()) { + sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED); + } + } + + public static void setThrowsInternalError(boolean throwsInternalError) { + ShadowRecoveryController.sThrowsInternalError = throwsInternalError; + } + + public static void setIsSupported(boolean isSupported) { + ShadowRecoveryController.sIsSupported = isSupported; + } + + @Resetter + public static void reset() { + sIsSupported = true; + sThrowsInternalError = false; + sKeysByAlias.clear(); + sKeyStatusesByAlias.clear(); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java new file mode 100644 index 000000000000..b2ec83583eba --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -0,0 +1,842 @@ +/* + * 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.job.controllers; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; +import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; +import static com.android.server.job.JobSchedulerService.RARE_INDEX; +import static com.android.server.job.JobSchedulerService.WORKING_INDEX; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.AlarmManager; +import android.app.job.JobInfo; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManagerInternal; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManagerInternal; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.JobSchedulerService.Constants; +import com.android.server.job.controllers.QuotaController.TimingSession; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.time.Clock; +import java.time.Duration; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class QuotaControllerTest { + private static final long SECOND_IN_MILLIS = 1000L; + private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; + private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; + private static final String TAG_CLEANUP = "*job.cleanup*"; + private static final String TAG_QUOTA_CHECK = "*job.quota_check*"; + private static final long IN_QUOTA_BUFFER_MILLIS = 30 * SECOND_IN_MILLIS; + private static final int CALLING_UID = 1000; + private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; + private static final int SOURCE_USER_ID = 0; + + private BroadcastReceiver mChargingReceiver; + private Constants mConstants; + private QuotaController mQuotaController; + + private MockitoSession mMockingSession; + @Mock + private AlarmManager mAlarmManager; + @Mock + private Context mContext; + @Mock + private JobSchedulerService mJobSchedulerService; + @Mock + private UsageStatsManagerInternal mUsageStatsManager; + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(LocalServices.class) + .startMocking(); + // Make sure constants turn on QuotaController. + mConstants = new Constants(); + mConstants.USE_HEARTBEATS = false; + + // Called in StateController constructor. + when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); + when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); + when(mJobSchedulerService.getConstants()).thenReturn(mConstants); + // Called in QuotaController constructor. + when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); + when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); + doReturn(mock(BatteryManagerInternal.class)) + .when(() -> LocalServices.getService(BatteryManagerInternal.class)); + doReturn(mUsageStatsManager) + .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); + // Used in JobStatus. + doReturn(mock(PackageManagerInternal.class)) + .when(() -> LocalServices.getService(PackageManagerInternal.class)); + + // Freeze the clocks at this moment in time + JobSchedulerService.sSystemClock = + Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); + JobSchedulerService.sUptimeMillisClock = + Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + + // Initialize real objects. + // Capture the listeners. + ArgumentCaptor<BroadcastReceiver> receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + mQuotaController = new QuotaController(mJobSchedulerService); + + verify(mContext).registerReceiver(receiverCaptor.capture(), any()); + mChargingReceiver = receiverCaptor.getValue(); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + private Clock getAdvancedClock(Clock clock, long incrementMs) { + return Clock.offset(clock, Duration.ofMillis(incrementMs)); + } + + private void advanceElapsedClock(long incrementMs) { + JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( + JobSchedulerService.sElapsedRealtimeClock, incrementMs); + } + + private void setCharging() { + Intent intent = new Intent(BatteryManager.ACTION_CHARGING); + mChargingReceiver.onReceive(mContext, intent); + } + + private void setDischarging() { + Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING); + mChargingReceiver.onReceive(mContext, intent); + } + + private void setStandbyBucket(int bucketIndex) { + int bucket; + switch (bucketIndex) { + case ACTIVE_INDEX: + bucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; + break; + case WORKING_INDEX: + bucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET; + break; + case FREQUENT_INDEX: + bucket = UsageStatsManager.STANDBY_BUCKET_FREQUENT; + break; + case RARE_INDEX: + bucket = UsageStatsManager.STANDBY_BUCKET_RARE; + break; + default: + bucket = UsageStatsManager.STANDBY_BUCKET_NEVER; + } + when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID), + anyLong())).thenReturn(bucket); + } + + private void setStandbyBucket(int bucketIndex, JobStatus job) { + setStandbyBucket(bucketIndex); + job.setStandbyBucket(bucketIndex); + } + + private JobStatus createJobStatus(String testTag, int jobId) { + JobInfo jobInfo = new JobInfo.Builder(jobId, + new ComponentName(mContext, "TestQuotaJobService")) + .setMinimumLatency(Math.abs(jobId) + 1) + .build(); + return JobStatus.createFromJobInfo( + jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag); + } + + private TimingSession createTimingSession(long start, long duration, int count) { + return new TimingSession(start, start + duration, count); + } + + @Test + public void testSaveTimingSession() { + assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); + + List<TimingSession> expected = new ArrayList<>(); + TimingSession one = new TimingSession(1, 10, 1); + TimingSession two = new TimingSession(11, 20, 2); + TimingSession thr = new TimingSession(21, 30, 3); + + mQuotaController.saveTimingSession(0, "com.android.test", one); + expected.add(one); + assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + + mQuotaController.saveTimingSession(0, "com.android.test", two); + expected.add(two); + assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + + mQuotaController.saveTimingSession(0, "com.android.test", thr); + expected.add(thr); + assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + } + + @Test + public void testDeleteObsoleteSessionsLocked() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + TimingSession one = createTimingSession( + now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); + TimingSession two = createTimingSession( + now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); + TimingSession thr = createTimingSession( + now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); + // Overlaps 24 hour boundary. + TimingSession fou = createTimingSession( + now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1); + // Way past the 24 hour boundary. + TimingSession fiv = createTimingSession( + now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); + List<TimingSession> expected = new ArrayList<>(); + // Added in correct (chronological) order. + expected.add(fou); + expected.add(thr); + expected.add(two); + expected.add(one); + mQuotaController.saveTimingSession(0, "com.android.test", fiv); + mQuotaController.saveTimingSession(0, "com.android.test", fou); + mQuotaController.saveTimingSession(0, "com.android.test", thr); + mQuotaController.saveTimingSession(0, "com.android.test", two); + mQuotaController.saveTimingSession(0, "com.android.test", one); + + mQuotaController.deleteObsoleteSessionsLocked(); + + assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + } + + @Test + public void testGetTrailingExecutionTimeLocked_NoTimer() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Added in chronological order. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3)); + + assertEquals(0, mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + MINUTE_IN_MILLIS)); + assertEquals(2 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 3 * MINUTE_IN_MILLIS)); + assertEquals(4 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 5 * MINUTE_IN_MILLIS)); + assertEquals(4 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 49 * MINUTE_IN_MILLIS)); + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 50 * MINUTE_IN_MILLIS)); + assertEquals(6 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + HOUR_IN_MILLIS)); + assertEquals(11 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 2 * HOUR_IN_MILLIS)); + assertEquals(12 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 3 * HOUR_IN_MILLIS)); + assertEquals(22 * MINUTE_IN_MILLIS, + mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test", + 6 * HOUR_IN_MILLIS)); + } + + @Test + public void testMaybeScheduleCleanupAlarmLocked() { + // No sessions saved yet. + mQuotaController.maybeScheduleCleanupAlarmLocked(); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any()); + + // Test with only one timing session saved. + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); + mQuotaController.saveTimingSession(0, "com.android.test", + new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1)); + mQuotaController.maybeScheduleCleanupAlarmLocked(); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); + + // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleCleanupAlarmLocked(); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); + } + + @Test + public void testMaybeScheduleStartAlarmLocked_WorkingSet() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + // Working set window size is 2 hours. + final int standbyBucket = WORKING_INDEX; + + // No sessions saved yet. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions out of window. + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions in window but still in quota. + final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); + // Counting backwards, the quota will come back one minute before the end. + final long expectedAlarmTime = + end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.saveTimingSession(0, "com.android.test", + new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Add some more sessions, but still in quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test when out of quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // Alarm already scheduled, so make sure it's not scheduled again. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + } + + @Test + public void testMaybeScheduleStartAlarmLocked_Frequent() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + // Frequent window size is 8 hours. + final int standbyBucket = FREQUENT_INDEX; + + // No sessions saved yet. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions out of window. + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions in window but still in quota. + final long start = now - (6 * HOUR_IN_MILLIS); + final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Add some more sessions, but still in quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test when out of quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // Alarm already scheduled, so make sure it's not scheduled again. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + } + + @Test + public void testMaybeScheduleStartAlarmLocked_Rare() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + // Rare window size is 24 hours. + final int standbyBucket = RARE_INDEX; + + // No sessions saved yet. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions out of window. + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions in window but still in quota. + final long start = now - (6 * HOUR_IN_MILLIS); + // Counting backwards, the first minute in the session is over the allowed time, so it + // needs to be excluded. + final long expectedAlarmTime = + start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Add some more sessions, but still in quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test when out of quota. + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1)); + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // Alarm already scheduled, so make sure it's not scheduled again. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + } + + /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */ + @Test + public void testMaybeScheduleStartAlarmLocked_BucketChange() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + // Affects rare bucket + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3)); + // Affects frequent and rare buckets + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3)); + // Affects working, frequent, and rare buckets + final long outOfQuotaTime = now - HOUR_IN_MILLIS; + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10)); + // Affects all buckets + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3)); + + InOrder inOrder = inOrder(mAlarmManager); + + // Start in ACTIVE bucket. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), + any()); + inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class)); + + // And down from there. + final long expectedWorkingAlarmTime = + outOfQuotaTime + (2 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + final long expectedFrequentAlarmTime = + outOfQuotaTime + (8 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + final long expectedRareAlarmTime = + outOfQuotaTime + (24 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS; + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // And back up again. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), + any()); + inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class)); + } + + /** Tests that QuotaController doesn't throttle if throttling is turned off. */ + @Test + public void testThrottleToggling() throws Exception { + setDischarging(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + 10 * MINUTE_IN_MILLIS, 4)); + JobStatus jobStatus = createJobStatus("testThrottleToggling", 1); + setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + mConstants.USE_HEARTBEATS = true; + mQuotaController.onConstantsUpdatedLocked(); + Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background. + assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + mConstants.USE_HEARTBEATS = false; + mQuotaController.onConstantsUpdatedLocked(); + Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background. + assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + } + + @Test + public void testConstantsUpdating_ValidValues() { + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS; + + mQuotaController.onConstantsUpdatedLocked(); + + assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); + assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); + assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); + assertEquals(45 * MINUTE_IN_MILLIS, + mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); + assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); + } + + @Test + public void testConstantsUpdating_InvalidValues() { + // Test negatives + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS; + + mQuotaController.onConstantsUpdatedLocked(); + + assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); + assertEquals(0, mQuotaController.getInQuotaBufferMs()); + assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); + assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); + assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); + assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); + + // Test larger than a day. Controller should cap at one day. + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS; + mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS; + + mQuotaController.onConstantsUpdatedLocked(); + + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); + assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); + } + + /** Tests that TimingSessions aren't saved when the device is charging. */ + @Test + public void testTimerTracking_Charging() { + setCharging(); + + JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** Tests that TimingSessions are saved properly when the device is discharging. */ + @Test + public void testTimerTracking_Discharging() { + setDischarging(); + + JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + List<TimingSession> expected = new ArrayList<>(); + + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Test overlapping jobs. + JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2); + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + + JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3); + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + + advanceElapsedClock(SECOND_IN_MILLIS); + + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.prepareForExecutionLocked(jobStatus2); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.prepareForExecutionLocked(jobStatus3); + advanceElapsedClock(20 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that TimingSessions are saved properly when the device alternates between + * charging and discharging. + */ + @Test + public void testTimerTracking_ChargingAndDischarging() { + JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2); + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3); + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // A job starting while charging. Only the portion that runs during the discharging period + // should be counted. + setCharging(); + + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setDischarging(); + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // One job starts while discharging, spans a charging session, and ends after the charging + // session. Only the portions during the discharging periods should be counted. This should + // result in two TimingSessions. A second job starts while discharging and ends within the + // charging session. Only the portion during the first discharging portion should be + // counted. A third job starts and ends within the charging session. The third job + // shouldn't be included in either job count. + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.prepareForExecutionLocked(jobStatus2); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setCharging(); + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); + mQuotaController.prepareForExecutionLocked(jobStatus3); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(20 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // A job starting while discharging and ending while charging. Only the portion that runs + // during the discharging period should be counted. + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + mQuotaController.prepareForExecutionLocked(jobStatus2); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setCharging(); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches + * its quota. + */ + @Test + public void testTracking_OutOfQuota() { + JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window + // Now the package only has two seconds to run. + final long remainingTimeMs = 2 * SECOND_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); + + // Start the job. + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + } + + /** + * Tests that a job is properly handled when it's at the edge of its quota and the old quota is + * being phased out. + */ + @Test + public void testTracking_RollingQuota() { + JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long remainingTimeMs = SECOND_IN_MILLIS; + // The package only has one second to run, but this session is at the edge of the rolling + // window, so as the package "reaches its quota" it will have more to keep running. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 2 * HOUR_IN_MILLIS, + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); + + assertEquals(remainingTimeMs, mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); + // Start the job. + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(); + assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + // The job used up the remaining quota, but in that time, the same amount of time in the + // old TimingSession also fell out of the quota window, so it should still have the same + // amount of remaining time left its quota. + assertEquals(remainingTimeMs, + mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(remainingTimeMs)); + } +} diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 746c4530a5e1..cf4d3a8070f9 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -66,6 +66,7 @@ <uses-permission android:name="android.permission.SUSPEND_APPS"/> <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> + <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java new file mode 100644 index 000000000000..f70efdfadfd7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java @@ -0,0 +1,125 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; + +import android.os.Binder; +import android.os.Process; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.os.BinderInternal; +import com.android.internal.os.BinderInternal.CallSession; +import com.android.server.BinderCallsStatsService.WorkSourceProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +@Presubmit +public class BinderCallsStatsServiceTest { + @Test + public void weTrustOurselves() { + WorkSourceProvider workSourceProvider = new WorkSourceProvider() { + protected int getCallingUid() { + return Process.myUid(); + } + + protected int getCallingWorkSourceUid() { + return 1; + } + }; + workSourceProvider.systemReady(InstrumentationRegistry.getContext()); + + assertEquals(1, workSourceProvider.resolveWorkSourceUid()); + } + + @Test + public void workSourceSetIfCallerHasPermission() { + WorkSourceProvider workSourceProvider = new WorkSourceProvider() { + protected int getCallingUid() { + // System process uid which as UPDATE_DEVICE_STATS. + return 1001; + } + + protected int getCallingWorkSourceUid() { + return 1; + } + }; + workSourceProvider.systemReady(InstrumentationRegistry.getContext()); + + assertEquals(1, workSourceProvider.resolveWorkSourceUid()); + } + + @Test + public void workSourceResolvedToCallingUid() { + WorkSourceProvider workSourceProvider = new WorkSourceProvider() { + protected int getCallingUid() { + // UID without permissions. + return Integer.MAX_VALUE; + } + + protected int getCallingWorkSourceUid() { + return 1; + } + }; + workSourceProvider.systemReady(InstrumentationRegistry.getContext()); + + assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid()); + } + + @Test + public void workSourceSet() { + TestObserver observer = new TestObserver(); + observer.callStarted(new Binder(), 0); + assertEquals(true, observer.workSourceSet); + } + + static class TestObserver extends BinderCallsStatsService.BinderCallsObserver { + public boolean workSourceSet = false; + + TestObserver() { + super(new NoopObserver(), new WorkSourceProvider()); + } + + @Override + protected void setThreadLocalWorkSourceUid(int uid) { + workSourceSet = true; + } + } + + + static class NoopObserver implements BinderInternal.Observer { + @Override + public CallSession callStarted(Binder binder, int code) { + return null; + } + + @Override + public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { + } + + @Override + public void callThrewException(CallSession s, Exception exception) { + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java index 81107cf2ef4f..2a78b6f6ca24 100644 --- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java @@ -17,9 +17,10 @@ package com.android.server; -import static org.mockito.Mockito.when; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import android.content.Context; import android.content.Intent; import android.os.BatteryManager; @@ -134,4 +135,44 @@ public class CachedDeviceStateServiceTest { mContext.sendBroadcast(intentUnplugged); assertThat(deviceState.isCharging()).isFalse(); } + + @Test + public void correctlyTracksTimeOnBattery() throws Exception { + CachedDeviceStateService service = new CachedDeviceStateService(mContext); + when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE); + + service.onStart(); + CachedDeviceState.Readonly deviceState = + LocalServices.getService(CachedDeviceState.Readonly.class); + + CachedDeviceState.TimeInStateStopwatch stopwatch = + deviceState.createTimeOnBatteryStopwatch(); + + // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY. + assertThat(stopwatch.isRunning()).isFalse(); + service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + + assertThat(stopwatch.isRunning()).isTrue(); + stopwatch.reset(); + + Thread.sleep(100); + assertThat(stopwatch.isRunning()).isTrue(); + assertThat(stopwatch.getMillis()).isAtLeast(100L); + + long timeOnBatteryBeforePluggedIn = stopwatch.getMillis(); + Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED); + intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC); + mContext.sendBroadcast(intentPluggedIn); + + assertThat(stopwatch.getMillis()).isAtLeast(timeOnBatteryBeforePluggedIn); + assertThat(stopwatch.isRunning()).isFalse(); + + long timeOnBatteryAfterPluggedIn = stopwatch.getMillis(); + Thread.sleep(20); + assertThat(stopwatch.getMillis()).isEqualTo(timeOnBatteryAfterPluggedIn); + + stopwatch.reset(); + assertThat(stopwatch.getMillis()).isEqualTo(0L); + assertThat(stopwatch.isRunning()).isFalse(); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java index 53711a623704..e0ecd3ee24f0 100644 --- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java @@ -35,6 +35,7 @@ import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; import com.android.internal.app.ColorDisplayController; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; @@ -911,7 +912,11 @@ public class ColorDisplayServiceTest { startService(); assertAccessibilityTransformActivated(true /* activated */ ); assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL); - assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC); + } } @Test @@ -926,7 +931,11 @@ public class ColorDisplayServiceTest { startService(); assertAccessibilityTransformActivated(true /* activated */ ); assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL); - assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC); + } } @Test @@ -942,7 +951,11 @@ public class ColorDisplayServiceTest { startService(); assertAccessibilityTransformActivated(true /* activated */ ); assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL); - assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED); + } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) { + assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC); + } } @Test @@ -1030,6 +1043,24 @@ public class ColorDisplayServiceTest { } /** + * Returns whether the color mode is valid on the device the tests are running on. + * + * @param mode the mode to check + */ + private boolean isColorModeValid(int mode) { + final int[] availableColorModes = mContext.getResources().getIntArray( + R.array.config_availableColorModes); + if (availableColorModes != null) { + for (int availableMode : availableColorModes) { + if (mode == availableMode) { + return true; + } + } + } + return false; + } + + /** * Convenience method to start {@link #mColorDisplayService}. */ private void startService() { @@ -1038,7 +1069,6 @@ public class ColorDisplayServiceTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { - mColorDisplayService.onStart(); mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); mColorDisplayService.onStartUser(mUserId); } diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 5077f9289477..c1963da3b3af 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -268,7 +268,7 @@ public class ThermalManagerServiceTest { @Test public void testGetCurrentStatus() throws RemoteException { - int status = Temperature.THROTTLING_WARNING; + int status = Temperature.THROTTLING_EMERGENCY; Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); mFakeHal.mCallback.onValues(newSkin); assertEquals(status, mService.mService.getCurrentThermalStatus()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 9da204fe3c02..41d5a1c2fac4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -456,6 +456,31 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } @Test + public void testNoBeepForImportanceDefaultInAutomotive() throws Exception { + mService.setIsAutomotive(true); + + NotificationRecord r = getBeepyNotification(); + r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + assertFalse(r.isInterruptive()); + } + + @Test + public void testBeepForImportanceHighInAutomotive() throws Exception { + mService.setIsAutomotive(true); + + NotificationRecord r = getBeepyNotification(); + + mService.buzzBeepBlinkLocked(r); + + verifyBeepLooped(); + assertTrue(r.isInterruptive()); + } + + @Test public void testNoInterruptionForMin() throws Exception { NotificationRecord r = getBeepyNotification(); r.setSystemImportance(NotificationManager.IMPORTANCE_MIN); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java index b30bb4b37c12..068129569d14 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java @@ -15,8 +15,9 @@ */ package com.android.server.notification; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -34,8 +35,8 @@ import android.os.Build; import android.os.UserHandle; import android.provider.Settings; import android.service.notification.StatusBarNotification; -import android.telecom.TelecomManager; import android.support.test.runner.AndroidJUnit4; +import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.UiServiceTestCase; @@ -211,7 +212,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "colorized", uid2, uid2, n13, new UserHandle(userId), "", 1999), getDefaultChannel()); - mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH); + mRecordColorized.setSystemImportance(NotificationManager.IMPORTANCE_HIGH); Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) @@ -225,11 +226,11 @@ public class NotificationComparatorTest extends UiServiceTestCase { } @Test - public void testOrdering() throws Exception { + public void testOrdering() { final List<NotificationRecord> expected = new ArrayList<>(); expected.add(mRecordColorizedCall); - expected.add(mRecordDefaultMedia); expected.add(mRecordColorized); + expected.add(mRecordDefaultMedia); expected.add(mRecordHighCall); expected.add(mRecordInlineReply); if (mRecordSms != null) { @@ -250,11 +251,11 @@ public class NotificationComparatorTest extends UiServiceTestCase { Collections.sort(actual, new NotificationComparator(mContext)); - assertEquals(expected, actual); + assertThat(actual, contains(expected.toArray())); } @Test - public void testMessaging() throws Exception { + public void testMessaging() { NotificationComparator comp = new NotificationComparator(mContext); assertTrue(comp.isImportantMessaging(mRecordInlineReply)); if (mRecordSms != null) { @@ -265,7 +266,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { } @Test - public void testPeople() throws Exception { + public void testPeople() { NotificationComparator comp = new NotificationComparator(mContext); assertTrue(comp.isImportantPeople(mRecordStarredContact)); assertTrue(comp.isImportantPeople(mRecordContact)); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 1d63c57e6cfe..7be331cc8a06 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -79,6 +79,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); when(mDisplayPolicy.getSystemUiContext()).thenReturn(context); when(mDisplayPolicy.hasNavigationBar()).thenReturn(true); + when(mDisplayPolicy.hasStatusBar()).thenReturn(true); final int shortSizeDp = Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 9569c0d5affa..105f8260022f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -22,7 +22,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; @@ -750,6 +753,64 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } @Test + public void testUsesDisplayOrientationForNoSensorOrientation() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + + mActivity.info.screenOrientation = SCREEN_ORIENTATION_NOSENSOR; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + final int orientationForDisplay = orientationFromBounds(freeformDisplay.getBounds()); + final int orientationForTask = orientationFromBounds(mResult.mBounds); + assertEquals("Launch bounds orientation should be the same as the display, but" + + " display orientation is " + + ActivityInfo.screenOrientationToString(orientationForDisplay) + + " launch bounds orientation is " + + ActivityInfo.screenOrientationToString(orientationForTask), + orientationForDisplay, orientationForTask); + } + + @Test + public void testRespectsAppRequestedOrientation_Landscape() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + + mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, orientationFromBounds(mResult.mBounds)); + } + + @Test + public void testRespectsAppRequestedOrientation_Portrait() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + + mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, orientationFromBounds(mResult.mBounds)); + } + + @Test public void testDefaultSizeSmallerThanBigScreen() { final TestActivityDisplay freeformDisplay = createNewActivityDisplay( WINDOWING_MODE_FREEFORM); @@ -1090,6 +1151,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { display.setWindowingMode(windowingMode); display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080); display.getConfiguration().densityDpi = DENSITY_DEFAULT; + display.getConfiguration().orientation = ORIENTATION_LANDSCAPE; return display; } @@ -1115,6 +1177,11 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } } + private int orientationFromBounds(Rect bounds) { + return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE + : SCREEN_ORIENTATION_PORTRAIT; + } + private static class WindowLayoutBuilder { private int mWidth = -1; private int mHeight = -1; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java index 4a99172160f5..50fd188cc00b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java @@ -30,6 +30,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; import android.content.Context; @@ -125,11 +126,12 @@ public class WindowManagerServiceRule implements TestRule { if (input != null && input.length > 1) { doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); } + ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); + when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock()); mService = WindowManagerService.main(context, ims, false, false, mPolicy = new TestWindowManagerPolicy( - WindowManagerServiceRule.this::getWindowManagerService), - new WindowManagerGlobalLock()); + WindowManagerServiceRule.this::getWindowManagerService), atms); mService.mTransactionFactory = () -> { final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); mSurfaceTransactions.add(new WeakReference<>(transaction)); @@ -173,21 +175,21 @@ public class WindowManagerServiceRule implements TestRule { }; } - public WindowManagerService getWindowManagerService() { + WindowManagerService getWindowManagerService() { return mService; } - public TestWindowManagerPolicy getWindowManagerPolicy() { - return mPolicy; - } - - public void waitUntilWindowManagerHandlersIdle() { + void waitUntilWindowManagerHandlersIdle() { final WindowManagerService wm = getWindowManagerService(); - if (wm != null) { - wm.mH.runWithScissors(() -> { }, 0); - wm.mAnimationHandler.runWithScissors(() -> { }, 0); - SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); + if (wm == null) { + return; } + wm.mH.removeCallbacksAndMessages(null); + wm.mAnimationHandler.removeCallbacksAndMessages(null); + SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null); + wm.mH.runWithScissors(() -> { }, 0); + wm.mAnimationHandler.runWithScissors(() -> { }, 0); + SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); } private void destroyAllSurfaceTransactions() { diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index cef998651cfe..d617de0af6a1 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -29,7 +29,6 @@ import android.os.ParcelFileDescriptor; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; -import java.lang.String; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; @@ -908,10 +907,16 @@ public final class Call { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("[pa: "); + sb.append("[id: "); + sb.append(mTelecomCallId); + sb.append(", pa: "); sb.append(mAccountHandle); sb.append(", hdl: "); - sb.append(Log.pii(mHandle)); + sb.append(Log.piiHandle(mHandle)); + sb.append(", hdlPres: "); + sb.append(mHandlePresentation); + sb.append(", videoState: "); + sb.append(VideoProfile.videoStateToString(mVideoState)); sb.append(", caps: "); sb.append(capabilitiesToString(mCallCapabilities)); sb.append(", props: "); diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 7db69407ad3d..662874316a7f 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -27,8 +27,8 @@ import android.os.Message; import android.os.RemoteException; import com.android.internal.os.SomeArgs; -import com.android.internal.telecom.ICallScreeningService; import com.android.internal.telecom.ICallScreeningAdapter; +import com.android.internal.telecom.ICallScreeningService; /** * This service can be implemented by the default dialer (see @@ -147,7 +147,7 @@ public abstract class CallScreeningService extends Service { private boolean mShouldSkipCallLog; private boolean mShouldSkipNotification; - /* + /** * Sets whether the incoming call should be blocked. */ public Builder setDisallowCall(boolean shouldDisallowCall) { @@ -155,7 +155,7 @@ public abstract class CallScreeningService extends Service { return this; } - /* + /** * Sets whether the incoming call should be disconnected as if the user had manually * rejected it. This property should only be set to true if the call is disallowed. */ @@ -164,16 +164,20 @@ public abstract class CallScreeningService extends Service { return this; } - /* + /** * Sets whether the incoming call should not be displayed in the call log. This property * should only be set to true if the call is disallowed. + * <p> + * Note: Calls will still be logged with type + * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property + * is set. */ public Builder setSkipCallLog(boolean shouldSkipCallLog) { mShouldSkipCallLog = shouldSkipCallLog; return this; } - /* + /** * Sets whether a missed call notification should not be shown for the incoming call. * This property should only be set to true if the call is disallowed. */ @@ -211,6 +215,17 @@ public abstract class CallScreeningService extends Service { * Called when a new incoming call is added. * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)} * should be called to allow or disallow the call. + * <p> + * Note: The {@link Call.Details} instance provided to a call screening service will only have + * the following properties set. The rest of the {@link Call.Details} properties will be set to + * their default value or {@code null}. + * <ul> + * <li>{@link Call.Details#getState()}</li> + * <li>{@link Call.Details#getConnectTimeMillis()}</li> + * <li>{@link Call.Details#getCreationTimeMillis()}</li> + * <li>{@link Call.Details#getHandle()}</li> + * <li>{@link Call.Details#getHandlePresentation()}</li> + * </ul> * * @param callDetails Information about a new incoming call, see {@link Call.Details}. */ diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 13fbeaaa02b7..ca0c854a1a75 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -118,6 +118,13 @@ public class ServiceState implements Parcelable { */ public static final int FREQUENCY_RANGE_MMWAVE = 4; + private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList( + FREQUENCY_RANGE_UNKNOWN, + FREQUENCY_RANGE_LOW, + FREQUENCY_RANGE_MID, + FREQUENCY_RANGE_HIGH, + FREQUENCY_RANGE_MMWAVE); + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "DUPLEX_MODE_", @@ -1835,4 +1842,13 @@ public class ServiceState implements Parcelable { mNetworkRegistrationStates.add(regState); } } + + /** + * @hide + */ + public static final int getBetterNRFrequencyRange(int range1, int range2) { + return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2) + ? range1 + : range2; + } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 2a01ac4b8df2..2c06c4720650 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1590,14 +1590,23 @@ public class SubscriptionManager { return subId; } - /** @hide */ - @UnsupportedAppUsage - public void setDefaultSmsSubId(int subId) { - if (VDBG) logd("setDefaultSmsSubId sub id = " + subId); + /** + * Set the subscription which will be used by default for SMS, with the subscription which + * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied + * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}). + * + * @param subscriptionId the supplied subscription ID + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setDefaultSmsSubId(int subscriptionId) { + if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId); try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - iSub.setDefaultSmsSubId(subId); + iSub.setDefaultSmsSubId(subscriptionId); } } catch (RemoteException ex) { // ignore it @@ -1645,14 +1654,23 @@ public class SubscriptionManager { return subId; } - /** @hide */ - @UnsupportedAppUsage - public void setDefaultDataSubId(int subId) { - if (VDBG) logd("setDataSubscription sub id = " + subId); + /** + * Set the subscription which will be used by default for data, with the subscription which + * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied + * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}). + * + * @param subscriptionId the supplied subscription ID + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setDefaultDataSubId(int subscriptionId) { + if (VDBG) logd("setDataSubscription sub id = " + subscriptionId); try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - iSub.setDefaultDataSubId(subId); + iSub.setDefaultDataSubId(subscriptionId); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 45d914e0dfdd..fa9b76de2e6b 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5541,19 +5541,40 @@ public class TelephonyManager { public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis, @NonNull @CallbackExecutor Executor executor, @NonNull NumberVerificationCallback callback) { + if (executor == null) { + throw new NullPointerException("Executor must be non-null"); + } + if (callback == null) { + throw new NullPointerException("Callback must be non-null"); + } + INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() { @Override - public void onCallReceived(String phoneNumber) throws RemoteException { - Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber)); + public void onCallReceived(String phoneNumber) { + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onCallReceived(phoneNumber))); } @Override - public void onVerificationFailed(int reason) throws RemoteException { - Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason)); + public void onVerificationFailed(int reason) { + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onVerificationFailed(reason))); } }; - // TODO -- call the aidl method + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.requestNumberVerification(range, timeoutMillis, internalCallback, + getOpPackageName()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "requestNumberVerification RemoteException", ex); + executor.execute(() -> + callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED)); + } } /** diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java index 3b1ef3f45993..994c49cd2315 100644 --- a/telephony/java/android/telephony/euicc/EuiccCardManager.java +++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java @@ -155,7 +155,7 @@ public class EuiccCardManager { * Requests all the profiles on eUicc. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and all the profiles. */ public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor, @@ -179,7 +179,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and profile. */ public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor, @@ -204,7 +204,7 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. * @param refresh Whether sending the REFRESH command to modem. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. */ public void disableProfile(String cardId, String iccid, boolean refresh, @@ -230,7 +230,7 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile to switch to. * @param refresh Whether sending the REFRESH command to modem. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and the EuiccProfileInfo enabled. */ public void switchToProfile(String cardId, String iccid, boolean refresh, @@ -255,7 +255,7 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. * @param nickname The nickname of the profile. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. */ public void setNickname(String cardId, String iccid, String nickname, @@ -279,7 +279,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. */ public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor, @@ -304,7 +304,7 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param options Bits of the options of resetting which parts of the eUICC memory. See * EuiccCard for details. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. */ public void resetMemory(String cardId, @ResetOption int options, @@ -327,7 +327,7 @@ public class EuiccCardManager { * Requests the default SM-DP+ address from eUICC. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and the default SM-DP+ address. */ public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor, @@ -350,7 +350,7 @@ public class EuiccCardManager { * Requests the SM-DS address from eUICC. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and the SM-DS address. */ public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor, @@ -374,7 +374,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param defaultSmdpAddress The default SM-DP+ address to set. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. */ public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, @@ -398,7 +398,7 @@ public class EuiccCardManager { * Requests Rules Authorisation Table. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the rule authorisation table. */ public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor, @@ -421,7 +421,7 @@ public class EuiccCardManager { * Requests the eUICC challenge for new profile downloading. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the challenge. */ public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor, @@ -444,7 +444,7 @@ public class EuiccCardManager { * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the info1. */ public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor, @@ -467,7 +467,7 @@ public class EuiccCardManager { * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading. * * @param cardId The Id of the eUICC. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the info2. */ public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor, @@ -500,7 +500,7 @@ public class EuiccCardManager { * GSMA RSP v2.0+. * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by * SM-DP+ server. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and a byte array which represents a * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+. */ @@ -540,7 +540,7 @@ public class EuiccCardManager { * SM-DP+ server. * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned * by SM-DP+ server. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and a byte array which represents a * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+ */ @@ -572,7 +572,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and a byte array which represents a * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+. */ @@ -601,7 +601,7 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param transactionId the transaction ID returned by SM-DP+ server. * @param reason the cancel reason. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and an byte[] which represents a * {@code CancelSessionResponse} defined in GSMA RSP v2.0+. */ @@ -630,7 +630,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param events bits of the event types ({@link EuiccNotification.Event}) to list. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the list of notifications. */ public void listNotifications(String cardId, @EuiccNotification.Event int events, @@ -654,7 +654,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param events bits of the event types ({@link EuiccNotification.Event}) to list. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the list of notifications. */ public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events, @@ -678,7 +678,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param seqNumber the sequence number of the notification. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code and the notification. */ public void retrieveNotification(String cardId, int seqNumber, @@ -702,7 +702,7 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param seqNumber the sequence number of the notification. - * @param executor The executor through which the callback should be invoke. + * @param executor The executor through which the callback should be invoked. * @param callback the callback to get the result code. */ public void removeNotificationFromList(String cardId, int seqNumber, diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 32e939a0c925..399dc5255176 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -35,6 +35,7 @@ import android.telephony.ICellInfoCallback; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; import android.telephony.NetworkScanRequest; +import android.telephony.PhoneNumberRange; import android.telephony.RadioAccessFamily; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -49,6 +50,7 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; +import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; import java.util.List; @@ -871,6 +873,17 @@ interface ITelephony { String getCdmaMin(int subId); /** + * Request that the next incoming call from a number matching {@code range} be intercepted. + * @param range The range of phone numbers the caller expects a phone call from. + * @param timeoutMillis The amount of time to wait for such a call, or + * {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser. + * @param callback the callback aidl + * @param callingPackage the calling package name. + */ + void requestNumberVerification(in PhoneNumberRange range, long timeoutMillis, + in INumberVerificationCallback callback, String callingPackage); + + /** * Has the calling application been granted special privileges by the carrier. * * If any of the packages in the calling UID has carrier privileges, the diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 4dc0341c8e2b..d8f961850906 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -542,7 +542,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecApplyTransportModeTransform( - eq(pfd.getFileDescriptor()), + eq(pfd), eq(mUid), eq(IpSecManager.DIRECTION_OUT), anyString(), @@ -555,7 +555,7 @@ public class IpSecServiceParameterizedTest { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); mIpSecService.removeTransportModeTransforms(pfd); - verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); } private IpSecTunnelInterfaceResponse createAndValidateTunnel( diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 2c94a601fbf6..724446e11c83 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -425,7 +425,7 @@ public class IpSecServiceTest { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); mIpSecService.removeTransportModeTransforms(pfd); - verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); } @Test @@ -620,10 +620,10 @@ public class IpSecServiceTest { mIpSecService.openUdpEncapsulationSocket(0, new Binder()); FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); - ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> { + ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> { try { StructStat sockStat = Os.fstat(sockFd); - StructStat argStat = Os.fstat(arg); + StructStat argStat = Os.fstat(arg.getFileDescriptor()); return sockStat.st_ino == argStat.st_ino && sockStat.st_dev == argStat.st_dev; diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index ed70fb3c57d6..df0daebe8453 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -240,6 +240,12 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { } break; + case android::RES_TABLE_OVERLAYABLE_TYPE: + if (!ParseOverlayable(parser.chunk())) { + return false; + } + break; + default: diag_->Warn(DiagMessage(source_) << "unexpected chunk type " @@ -383,24 +389,12 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, return false; } - const uint32_t type_spec_flags = entry_type_spec_flags_[res_id]; - if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 || - (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) { - if (entry->flags & ResTable_entry::FLAG_PUBLIC) { - Visibility visibility; - visibility.level = Visibility::Level::kPublic; - visibility.source = source_.WithLine(0); - if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) { - return false; - } - } - - if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) { - Overlayable overlayable; - overlayable.source = source_.WithLine(0); - if (!table_->AddOverlayableMangled(name, overlayable, diag_)) { - return false; - } + if (entry->flags & ResTable_entry::FLAG_PUBLIC) { + Visibility visibility; + visibility.level = Visibility::Level::kPublic; + visibility.source = source_.WithLine(0); + if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) { + return false; } // Erase the ID from the map once processed, so that we don't mark the same symbol more than @@ -433,6 +427,72 @@ bool BinaryResourceParser::ParseLibrary(const ResChunk_header* chunk) { return true; } +bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { + const ResTable_overlayable_header* header = ConvertTo<ResTable_overlayable_header>(chunk); + if (!header) { + diag_->Error(DiagMessage(source_) << "corrupt ResTable_category_header chunk"); + return false; + } + + ResChunkPullParser parser(GetChunkData(chunk), + GetChunkDataLen(chunk)); + while (ResChunkPullParser::IsGoodEvent(parser.Next())) { + if (util::DeviceToHost16(parser.chunk()->type) == android::RES_TABLE_OVERLAYABLE_POLICY_TYPE) { + const ResTable_overlayable_policy_header* policy_header = + ConvertTo<ResTable_overlayable_policy_header>(parser.chunk()); + + std::vector<Overlayable::Policy> policies; + if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) { + policies.push_back(Overlayable::Policy::kPublic); + } + if (policy_header->policy_flags + & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) { + policies.push_back(Overlayable::Policy::kSystem); + } + if (policy_header->policy_flags + & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) { + policies.push_back(Overlayable::Policy::kVendor); + } + if (policy_header->policy_flags + & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) { + policies.push_back(Overlayable::Policy::kProduct); + } + if (policy_header->policy_flags + & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) { + policies.push_back(Overlayable::Policy::kProductServices); + } + + const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>( + ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize)); + const ResTable_ref* const ref_end = ref_begin + + util::DeviceToHost32(policy_header->entry_count); + for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) { + ResourceId res_id(util::DeviceToHost32(ref_iter->ident)); + const auto iter = id_index_.find(res_id); + + // If the overlayable chunk comes before the type chunks, the resource ids and resource name + // pairing will not exist at this point. + if (iter == id_index_.cend()) { + diag_->Error(DiagMessage(source_) << "failed to find resource name for overlayable" + << " resource " << res_id); + return false; + } + + for (Overlayable::Policy policy : policies) { + Overlayable overlayable; + overlayable.source = source_.WithLine(0); + overlayable.policy = policy; + if (!table_->AddOverlayable(iter->second, overlayable, diag_)) { + return false; + } + } + } + } + } + + return true; +} + std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name, const ConfigDescription& config, const android::Res_value& value) { diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h index 2bdc051f4e29..a2eee5006964 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.h +++ b/tools/aapt2/format/binary/BinaryResourceParser.h @@ -54,6 +54,7 @@ class BinaryResourceParser { bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk); bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk); bool ParseLibrary(const android::ResChunk_header* chunk); + bool ParseOverlayable(const android::ResChunk_header* chunk); std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const android::ConfigDescription& config, diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 6c1a9ba2cbad..976c3288bfca 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -24,6 +24,7 @@ #include "android-base/logging.h" #include "android-base/macros.h" #include "android-base/stringprintf.h" +#include "androidfw/ResourceUtils.h" #include "ResourceTable.h" #include "ResourceValues.h" @@ -216,6 +217,11 @@ class MapFlattenVisitor : public ValueVisitor { size_t entry_count_ = 0; }; +struct PolicyChunk { + uint32_t policy_flags; + std::set<ResourceId> ids; +}; + class PackageFlattener { public: PackageFlattener(IAaptContext* context, ResourceTablePackage* package, @@ -267,6 +273,8 @@ class PackageFlattener { FlattenLibrarySpec(buffer); } + FlattenOverlayable(buffer); + pkg_writer.Finish(); return true; } @@ -413,6 +421,97 @@ class PackageFlattener { return sorted_entries; } + void FlattenOverlayable(BigBuffer* buffer) { + std::vector<PolicyChunk> policies; + + CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>"; + for (auto& type : package_->types) { + CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>"; + for (auto& entry : type->entries) { + CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>"; + + // TODO(b/120298168): Convert the policies vector to a policy set or bitmask + if (!entry->overlayable_declarations.empty()) { + uint16_t policy_flags = 0; + for (Overlayable overlayable : entry->overlayable_declarations) { + if (overlayable.policy) { + switch (overlayable.policy.value()) { + case Overlayable::Policy::kPublic: + policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; + break; + case Overlayable::Policy::kSystem: + policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION; + break; + case Overlayable::Policy::kVendor: + policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION; + break; + case Overlayable::Policy::kProduct: + policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; + break; + case Overlayable::Policy::kProductServices: + policy_flags |= + ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION; + break; + } + } else { + // Encode overlayable entries defined without a policy as publicly overlayable + policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; + } + } + + // Find the overlayable policy chunk with the same policies as the entry + PolicyChunk* policy_chunk = nullptr; + for (PolicyChunk& policy : policies) { + if (policy.policy_flags == policy_flags) { + policy_chunk = &policy; + break; + } + } + + // Create a new policy chunk if an existing one with the same policy cannot be found + if (policy_chunk == nullptr) { + PolicyChunk p; + p.policy_flags = policy_flags; + policies.push_back(p); + policy_chunk = &policies.back(); + } + + policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(), + entry->id.value())); + } + } + } + + if (policies.empty()) { + // Only write the overlayable chunk if the APK has overlayable entries + return; + } + + ChunkWriter writer(buffer); + writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE); + + // Write each policy block for the overlayable + for (PolicyChunk& policy : policies) { + ChunkWriter policy_writer(buffer); + ResTable_overlayable_policy_header* policy_type = + policy_writer.StartChunk<ResTable_overlayable_policy_header>( + RES_TABLE_OVERLAYABLE_POLICY_TYPE); + policy_type->policy_flags = util::HostToDevice32(policy.policy_flags); + policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size())); + + // Write the ids after the policy header + ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size()); + for (const ResourceId& id : policy.ids) { + id_block->ident = util::HostToDevice32(id.id); + id_block++; + } + + policy_writer.Finish(); + } + + writer.Finish(); + } + bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries, BigBuffer* buffer) { ChunkWriter type_spec_writer(buffer); @@ -446,11 +545,6 @@ class PackageFlattener { config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC); } - if (!entry->overlayable_declarations.empty()) { - config_masks[entry->id.value()] |= - util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE); - } - const size_t config_count = entry->values.size(); for (size_t i = 0; i < config_count; i++) { const ConfigDescription& config = entry->values[i]->config; diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index cd1414c7e628..410efbe83b1b 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -628,24 +628,108 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) { } TEST_F(TableFlattenerTest, FlattenOverlayable) { + std::string name = "com.app.test:integer/overlayable"; std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) - .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000)) + .AddSimple(name, ResourceId(0x7f020000)) + .AddOverlayable(name, Overlayable::Policy::kProduct) + .AddOverlayable(name, Overlayable::Policy::kSystem) + .AddOverlayable(name, Overlayable::Policy::kVendor) .Build(); - ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"), - Overlayable{}, test::GetDiagnostics())); + ResourceTable output_table; + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table)); - ResTable res_table; - ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); + auto search_result = output_table.FindResource(test::ParseNameOrDie(name)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy, + Overlayable::Policy::kSystem); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy, + Overlayable::Policy::kVendor); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy, + Overlayable::Policy::kProduct); +} + +TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { + std::string name_zero = "com.app.test:integer/overlayable_zero"; + std::string name_one = "com.app.test:integer/overlayable_one"; + std::string name_two = "com.app.test:integer/overlayable_two"; + std::string name_three = "com.app.test:integer/overlayable_three"; + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("com.app.test", 0x7f) + .AddSimple(name_zero, ResourceId(0x7f020000)) + .AddOverlayable(name_zero, Overlayable::Policy::kProduct) + .AddOverlayable(name_zero, Overlayable::Policy::kSystem) + .AddOverlayable(name_zero, Overlayable::Policy::kProductServices) + .AddSimple(name_one, ResourceId(0x7f020001)) + .AddOverlayable(name_one, Overlayable::Policy::kPublic) + .AddOverlayable(name_one, Overlayable::Policy::kSystem) + .AddSimple(name_two, ResourceId(0x7f020002)) + .AddOverlayable(name_two, Overlayable::Policy::kProduct) + .AddOverlayable(name_two, Overlayable::Policy::kSystem) + .AddOverlayable(name_two, Overlayable::Policy::kProductServices) + .AddSimple(name_three, ResourceId(0x7f020003)) + .AddOverlayable(name_three, {}) + .Build(); + + ResourceTable output_table; + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table)); + + auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy, + Overlayable::Policy::kSystem); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy, + Overlayable::Policy::kProduct); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy, + Overlayable::Policy::kProductServices); + + search_result = output_table.FindResource(test::ParseNameOrDie(name_one)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy, + Overlayable::Policy::kPublic); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy, + Overlayable::Policy::kSystem); + + search_result = output_table.FindResource(test::ParseNameOrDie(name_two)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy, + Overlayable::Policy::kSystem); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy, + Overlayable::Policy::kProduct); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy, + Overlayable::Policy::kProductServices); + + search_result = output_table.FindResource(test::ParseNameOrDie(name_three)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1); + EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy); + EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy, + Overlayable::Policy::kPublic); - const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable"); - uint32_t spec_flags = 0u; - ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr, - 0u, nullptr, 0u, &spec_flags), - Gt(0u)); - EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE); } + } // namespace aapt diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 3a5d5858254d..1b6626a8dfe9 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -368,7 +368,16 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) { // Symbol state information may be lost if there is no value for the resource. if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) { context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source) - << "no definition for declared symbol '" << name << "'"); + << "no definition for declared symbol '" << name + << "'"); + error = true; + } + + // Ensure that definitions for values declared as overlayable exist + if (!entry->overlayable_declarations.empty() && entry->values.empty()) { + context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source) + << "no definition for overlayable symbol '" + << name << "'"); error = true; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 8e0d4acf089d..cad6d2997bfb 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -156,33 +156,39 @@ public class WifiManager { public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; /** + * Reason code if the user has disallowed "android:change_wifi_state" app-ops from the app. + * @see android.app.AppOpsManager#unsafeCheckOp(String, int, String). + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; + + /** * Reason code if one or more of the network suggestions added already exists in platform's * database. * @see WifiNetworkSuggestion#equals(Object) */ - public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; /** * Reason code if the number of network suggestions provided by the app crosses the max * threshold set per app. * @see #getMaxNumberOfNetworkSuggestionsPerApp() */ - public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; /** * Reason code if one or more of the network suggestions removed does not exist in platform's * database. */ - public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = { STATUS_NETWORK_SUGGESTIONS_SUCCESS, STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL, + STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP, STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID, }) - @Retention(RetentionPolicy.SOURCE) public @interface NetworkSuggestionsStatusCode {} @@ -1982,6 +1988,8 @@ public class WifiManager { public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B /** @hide */ public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open + /** @hide */ + public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes private int getSupportedFeatures() { try { diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index fc5caf0a47d7..acc0518dbca4 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -357,11 +357,12 @@ public class WifiScanner { */ private int mBucketsScanned; /** - * Indicates that the scan results received are as a result of a scan of all available - * channels. This should only be expected to function for single scans. + * Bands scanned. One of the WIFI_BAND values. + * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover + * any of the bands. * {@hide} */ - private boolean mAllChannelsScanned; + private int mBandScanned; /** all scan results discovered in this scan, sorted by timestamp in ascending order */ private ScanResult mResults[]; @@ -374,12 +375,12 @@ public class WifiScanner { } /** {@hide} */ - public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned, - ScanResult[] results) { + public ScanData(int id, int flags, int bucketsScanned, int bandScanned, + ScanResult[] results) { mId = id; mFlags = flags; mBucketsScanned = bucketsScanned; - mAllChannelsScanned = allChannelsScanned; + mBandScanned = bandScanned; mResults = results; } @@ -387,7 +388,7 @@ public class WifiScanner { mId = s.mId; mFlags = s.mFlags; mBucketsScanned = s.mBucketsScanned; - mAllChannelsScanned = s.mAllChannelsScanned; + mBandScanned = s.mBandScanned; mResults = new ScanResult[s.mResults.length]; for (int i = 0; i < s.mResults.length; i++) { ScanResult result = s.mResults[i]; @@ -410,8 +411,8 @@ public class WifiScanner { } /** {@hide} */ - public boolean isAllChannelsScanned() { - return mAllChannelsScanned; + public int getBandScanned() { + return mBandScanned; } public ScanResult[] getResults() { @@ -429,7 +430,7 @@ public class WifiScanner { dest.writeInt(mId); dest.writeInt(mFlags); dest.writeInt(mBucketsScanned); - dest.writeInt(mAllChannelsScanned ? 1 : 0); + dest.writeInt(mBandScanned); dest.writeInt(mResults.length); for (int i = 0; i < mResults.length; i++) { ScanResult result = mResults[i]; @@ -447,13 +448,13 @@ public class WifiScanner { int id = in.readInt(); int flags = in.readInt(); int bucketsScanned = in.readInt(); - boolean allChannelsScanned = in.readInt() != 0; + int bandScanned = in.readInt(); int n = in.readInt(); ScanResult results[] = new ScanResult[n]; for (int i = 0; i < n; i++) { results[i] = ScanResult.CREATOR.createFromParcel(in); } - return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results); + return new ScanData(id, flags, bucketsScanned, bandScanned, results); } public ScanData[] newArray(int size) { @@ -759,6 +760,7 @@ public class WifiScanner { * Multiple requests should also not share this object. * {@hide} */ + @RequiresPermission(Manifest.permission.NETWORK_STACK) public void registerScanListener(ScanListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); int key = addListener(listener); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index b0ed11034810..e0442f2fd5ae 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.content.Context; @@ -1695,7 +1694,6 @@ public class WifiP2pManager { * @param listener for callback on success or failure. Can be null. * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) { checkChannel(c); diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index da42dcf623c2..cf1ed8fa84c0 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -16,6 +16,7 @@ package android.net.wifi; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -26,6 +27,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.WifiScanner.PnoSettings; import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork; +import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanSettings; import android.os.Handler; import android.os.Parcel; @@ -203,4 +205,29 @@ public class WifiScannerTest { assertNotNull(pnoNetwork.frequencies); } + /** + * Verify parcel read/write for ScanData. + */ + @Test + public void verifyScanDataParcel() throws Exception { + ScanData writeScanData = new ScanData(2, 0, 3, + WifiScanner.WIFI_BAND_BOTH_WITH_DFS, new ScanResult[0]); + + ScanData readScanData = parcelWriteRead(writeScanData); + assertEquals(writeScanData.getId(), readScanData.getId()); + assertEquals(writeScanData.getFlags(), readScanData.getFlags()); + assertEquals(writeScanData.getBucketsScanned(), readScanData.getBucketsScanned()); + assertEquals(writeScanData.getBandScanned(), readScanData.getBandScanned()); + assertArrayEquals(writeScanData.getResults(), readScanData.getResults()); + } + + /** + * Write the provided {@link ScanData} to a parcel and deserialize it. + */ + private static ScanData parcelWriteRead(ScanData writeScanData) throws Exception { + Parcel parcel = Parcel.obtain(); + writeScanData.writeToParcel(parcel, 0); + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + return ScanData.CREATOR.createFromParcel(parcel); + } } |