Merge "Fix resource by name lookup" into pi-dev
diff --git a/Android.mk b/Android.mk
index ee8fbe0..e2f88e8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -861,42 +861,39 @@
# ==== hiddenapi lists =======================================
-# Copy light and dark greylist over into the build folder.
+# Copy blacklist and light greylist over into the build folder.
# This is for ART buildbots which need to mock these lists and have alternative
# rules for building them. Other rules in the build system should depend on the
# files in the build folder.
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
+
# Temporarily merge light greylist from two files. Vendor list will become dark
# grey once we remove the UI toast.
$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): frameworks/base/config/hiddenapi-light-greylist.txt \
frameworks/base/config/hiddenapi-vendor-list.txt
sort $^ > $@
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
-
# Generate dark greylist as private API minus (blacklist plus light greylist).
-$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
- if [ ! -z "`comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "There should be no overlap between $(LIGHT_GREYLIST) and $(DARK_GREYLIST)" 1>&2; \
- comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST)) 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
exit 1; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
+ echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 2; \
elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
- comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST)) 1>&2; \
- exit 2; \
- elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
- comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST)) 1>&2; \
exit 3; \
fi
- comm -23 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST) $(DARK_GREYLIST)) > $@
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
# Include subdirectory makefiles
# ============================================================
diff --git a/api/current.txt b/api/current.txt
index 5cfd29a..dc3ab4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1378,7 +1378,7 @@
field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
field public static final int textEditSuggestionItemLayout = 16843636; // 0x1010374
field public static final int textFilterEnabled = 16843007; // 0x10100ff
- field public static final int textFontWeight = 16844166; // 0x1010586
+ field public static final int textFontWeight = 16844165; // 0x1010585
field public static final int textIsSelectable = 16843542; // 0x1010316
field public static final int textOff = 16843045; // 0x1010125
field public static final int textOn = 16843044; // 0x1010124
@@ -1469,7 +1469,6 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int urlBarResourceId = 16844164; // 0x1010584
field public static final int use32bitAbi = 16844053; // 0x1010515
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
@@ -1549,7 +1548,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
- field public static final int windowLayoutInDisplayCutoutMode = 16844167; // 0x1010587
+ field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
@@ -11167,6 +11166,7 @@
method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+ method public android.os.PersistableBundle getSuspendedPackageAppExtras();
method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method public abstract java.lang.String[] getSystemSharedLibraryNames();
method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -11180,6 +11180,7 @@
method public abstract boolean hasSystemFeature(java.lang.String, int);
method public abstract boolean isInstantApp();
method public abstract boolean isInstantApp(java.lang.String);
+ method public boolean isPackageSuspended();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -13645,12 +13646,14 @@
field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
field public static final int ALLOCATOR_SOFTWARE = 1; // 0x1
- field public static final int ERROR_SOURCE_ERROR = 3; // 0x3
- field public static final int ERROR_SOURCE_EXCEPTION = 1; // 0x1
- field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
}
- public static abstract class ImageDecoder.Error implements java.lang.annotation.Annotation {
+ public static final class ImageDecoder.DecodeException extends java.io.IOException {
+ method public int getError();
+ method public android.graphics.ImageDecoder.Source getSource();
+ field public static final int SOURCE_EXCEPTION = 1; // 0x1
+ field public static final int SOURCE_INCOMPLETE = 2; // 0x2
+ field public static final int SOURCE_MALFORMED_DATA = 3; // 0x3
}
public static class ImageDecoder.ImageInfo {
@@ -13659,16 +13662,12 @@
method public boolean isAnimated();
}
- public static class ImageDecoder.IncompleteException extends java.io.IOException {
- ctor public ImageDecoder.IncompleteException();
- }
-
public static abstract interface ImageDecoder.OnHeaderDecodedListener {
method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source);
}
public static abstract interface ImageDecoder.OnPartialImageListener {
- method public abstract boolean onPartialImage(int, android.graphics.ImageDecoder.Source);
+ method public abstract boolean onPartialImage(android.graphics.ImageDecoder.DecodeException);
}
public static abstract class ImageDecoder.Source {
@@ -16450,8 +16449,8 @@
}
public final class SessionConfiguration {
- ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler);
- method public android.os.Handler getHandler();
+ ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
+ method public java.util.concurrent.Executor getExecutor();
method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
method public android.hardware.camera2.CaptureRequest getSessionParameters();
@@ -21546,10 +21545,10 @@
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
method public double getAutomaticGainControlLevelDb();
- method public long getCarrierCycles();
+ method public deprecated long getCarrierCycles();
method public float getCarrierFrequencyHz();
- method public double getCarrierPhase();
- method public double getCarrierPhaseUncertainty();
+ method public deprecated double getCarrierPhase();
+ method public deprecated double getCarrierPhaseUncertainty();
method public double getCn0DbHz();
method public int getConstellationType();
method public int getMultipathIndicator();
@@ -21562,13 +21561,15 @@
method public int getSvid();
method public double getTimeOffsetNanos();
method public boolean hasAutomaticGainControlLevelDb();
- method public boolean hasCarrierCycles();
+ method public deprecated boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
- method public boolean hasCarrierPhase();
- method public boolean hasCarrierPhaseUncertainty();
+ method public deprecated boolean hasCarrierPhase();
+ method public deprecated boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
+ field public static final int ADR_STATE_HALF_CYCLE_REPORTED = 16; // 0x10
+ field public static final int ADR_STATE_HALF_CYCLE_RESOLVED = 8; // 0x8
field public static final int ADR_STATE_RESET = 2; // 0x2
field public static final int ADR_STATE_UNKNOWN = 0; // 0x0
field public static final int ADR_STATE_VALID = 1; // 0x1
@@ -22090,7 +22091,6 @@
method public boolean isBluetoothScoOn();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
- method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
@@ -22416,7 +22416,6 @@
method public int reloadStaticData();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
- method public void removeStreamEventCallback();
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -22431,7 +22430,6 @@
method public int setPresentation(android.media.AudioPresentation);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
- method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
method public int setVolume(float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -22467,7 +22465,6 @@
method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
- method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method public android.media.AudioTrack.Builder setPerformanceMode(int);
method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22491,12 +22488,6 @@
method public default void onRoutingChanged(android.media.AudioRouting);
}
- public static abstract class AudioTrack.StreamEventCallback {
- method public void onStreamDataRequest(android.media.AudioTrack);
- method public void onStreamPresentationEnd(android.media.AudioTrack);
- method public void onTearDown(android.media.AudioTrack);
- }
-
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -22549,40 +22540,6 @@
field public static final int QUALITY_MEDIUM = 1; // 0x1
}
- public final class DataSourceDesc {
- method public long getEndPosition();
- method public java.io.FileDescriptor getFileDescriptor();
- method public long getFileDescriptorLength();
- method public long getFileDescriptorOffset();
- method public android.media.Media2DataSource getMedia2DataSource();
- method public java.lang.String getMediaId();
- method public long getStartPosition();
- method public int getType();
- method public android.net.Uri getUri();
- method public android.content.Context getUriContext();
- method public java.util.List<java.net.HttpCookie> getUriCookies();
- method public java.util.Map<java.lang.String, java.lang.String> getUriHeaders();
- field public static final long LONG_MAX = 576460752303423487L; // 0x7ffffffffffffffL
- field public static final int TYPE_CALLBACK = 1; // 0x1
- field public static final int TYPE_FD = 2; // 0x2
- field public static final int TYPE_NONE = 0; // 0x0
- field public static final int TYPE_URI = 3; // 0x3
- }
-
- public static class DataSourceDesc.Builder {
- ctor public DataSourceDesc.Builder();
- ctor public DataSourceDesc.Builder(android.media.DataSourceDesc);
- method public android.media.DataSourceDesc build();
- method public android.media.DataSourceDesc.Builder setDataSource(android.media.Media2DataSource);
- method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor);
- method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor, long, long);
- method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri);
- method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>);
- method public android.media.DataSourceDesc.Builder setEndPosition(long);
- method public android.media.DataSourceDesc.Builder setMediaId(java.lang.String);
- method public android.media.DataSourceDesc.Builder setStartPosition(long);
- }
-
public final class DeniedByServerException extends android.media.MediaDrmException {
ctor public DeniedByServerException(java.lang.String);
}
@@ -22860,12 +22817,6 @@
method public abstract void onJetUserIdUpdate(android.media.JetPlayer, int, int);
}
- public abstract class Media2DataSource implements java.io.Closeable {
- ctor public Media2DataSource();
- method public abstract long getSize() throws java.io.IOException;
- method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
- }
-
public class MediaActionSound {
ctor public MediaActionSound();
method public void load(int);
@@ -22877,27 +22828,6 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
- public class MediaBrowser2 extends android.media.MediaController2 {
- ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback);
- method public void getChildren(java.lang.String, int, int, android.os.Bundle);
- method public void getItem(java.lang.String);
- method public void getLibraryRoot(android.os.Bundle);
- method public void getSearchResult(java.lang.String, int, int, android.os.Bundle);
- method public void search(java.lang.String, android.os.Bundle);
- method public void subscribe(java.lang.String, android.os.Bundle);
- method public void unsubscribe(java.lang.String);
- }
-
- public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback {
- ctor public MediaBrowser2.BrowserCallback();
- method public void onChildrenChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
- method public void onGetChildrenDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
- method public void onGetItemDone(android.media.MediaBrowser2, java.lang.String, android.media.MediaItem2);
- method public void onGetLibraryRootDone(android.media.MediaBrowser2, android.os.Bundle, java.lang.String, android.os.Bundle);
- method public void onGetSearchResultDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
- method public void onSearchResultChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
- }
-
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
@@ -23375,82 +23305,6 @@
field public static final int REGULAR_CODECS = 0; // 0x0
}
- public class MediaController2 implements java.lang.AutoCloseable {
- ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public void adjustVolume(int, int);
- method public void close();
- method public void fastForward();
- method public long getBufferedPosition();
- method public android.media.MediaItem2 getCurrentMediaItem();
- method public android.media.MediaController2.PlaybackInfo getPlaybackInfo();
- method public float getPlaybackSpeed();
- method public int getPlayerState();
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public long getPosition();
- method public int getRepeatMode();
- method public android.app.PendingIntent getSessionActivity();
- method public android.media.SessionToken2 getSessionToken();
- method public int getShuffleMode();
- method public boolean isConnected();
- method public void pause();
- method public void play();
- method public void playFromMediaId(java.lang.String, android.os.Bundle);
- method public void playFromSearch(java.lang.String, android.os.Bundle);
- method public void playFromUri(android.net.Uri, android.os.Bundle);
- method public void prepare();
- method public void prepareFromMediaId(java.lang.String, android.os.Bundle);
- method public void prepareFromSearch(java.lang.String, android.os.Bundle);
- method public void prepareFromUri(android.net.Uri, android.os.Bundle);
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void rewind();
- method public void seekTo(long);
- method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void setPlaybackSpeed(float);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRating(java.lang.String, android.media.Rating2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void setVolumeTo(int, int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public void stop();
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- }
-
- public static abstract class MediaController2.ControllerCallback {
- ctor public MediaController2.ControllerCallback();
- method public void onAllowedCommandsChanged(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
- method public void onBufferedPositionChanged(android.media.MediaController2, long);
- method public void onConnected(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
- method public void onCurrentMediaItemChanged(android.media.MediaController2, android.media.MediaItem2);
- method public void onCustomCommand(android.media.MediaController2, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void onCustomLayoutChanged(android.media.MediaController2, java.util.List<android.media.MediaSession2.CommandButton>);
- method public void onDisconnected(android.media.MediaController2);
- method public void onError(android.media.MediaController2, int, android.os.Bundle);
- method public void onPlaybackInfoChanged(android.media.MediaController2, android.media.MediaController2.PlaybackInfo);
- method public void onPlaybackSpeedChanged(android.media.MediaController2, float);
- method public void onPlayerStateChanged(android.media.MediaController2, int);
- method public void onPlaylistChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onPositionChanged(android.media.MediaController2, long, long);
- method public void onRepeatModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
- method public void onShuffleModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
- }
-
- public static final class MediaController2.PlaybackInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public int getControlType();
- method public int getCurrentVolume();
- method public int getMaxVolume();
- method public int getPlaybackType();
- field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
- field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
- }
-
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
@@ -23851,71 +23705,6 @@
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
- public class MediaItem2 {
- method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle);
- method public android.media.DataSourceDesc getDataSourceDesc();
- method public int getFlags();
- method public java.lang.String getMediaId();
- method public android.media.MediaMetadata2 getMetadata();
- method public boolean isBrowsable();
- method public boolean isPlayable();
- method public void setMetadata(android.media.MediaMetadata2);
- method public android.os.Bundle toBundle();
- field public static final int FLAG_BROWSABLE = 1; // 0x1
- field public static final int FLAG_PLAYABLE = 2; // 0x2
- }
-
- public static final class MediaItem2.Builder {
- ctor public MediaItem2.Builder(android.content.Context, int);
- method public android.media.MediaItem2 build();
- method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc);
- method public android.media.MediaItem2.Builder setMediaId(java.lang.String);
- method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2);
- }
-
- public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 {
- ctor public MediaLibraryService2();
- method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
- }
-
- public static final class MediaLibraryService2.LibraryRoot {
- ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle);
- method public android.os.Bundle getExtras();
- method public java.lang.String getRootId();
- field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
- field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT";
- field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
- }
-
- public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 {
- method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
- method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle);
- method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
- }
-
- public static final class MediaLibraryService2.MediaLibrarySession.Builder {
- ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
- method public android.media.MediaLibraryService2.MediaLibrarySession build();
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlayer(android.media.MediaPlayerBase);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
- method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
- }
-
- public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback {
- ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context);
- method public java.util.List<android.media.MediaItem2> onGetChildren(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
- method public android.media.MediaItem2 onGetItem(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
- method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, android.os.Bundle);
- method public java.util.List<android.media.MediaItem2> onGetSearchResult(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
- method public void onSearch(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onSubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onUnsubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
- }
-
public final class MediaMetadata implements android.os.Parcelable {
method public boolean containsKey(java.lang.String);
method public int describeContents();
@@ -23971,79 +23760,6 @@
method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
}
- public final class MediaMetadata2 {
- method public boolean containsKey(java.lang.String);
- method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle);
- method public android.graphics.Bitmap getBitmap(java.lang.String);
- method public android.os.Bundle getExtras();
- method public float getFloat(java.lang.String);
- method public long getLong(java.lang.String);
- method public java.lang.String getMediaId();
- method public android.media.Rating2 getRating(java.lang.String);
- method public java.lang.String getString(java.lang.String);
- method public java.lang.CharSequence getText(java.lang.String);
- method public java.util.Set<java.lang.String> keySet();
- method public int size();
- method public android.os.Bundle toBundle();
- field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
- field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
- field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
- field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
- field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
- field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
- field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
- field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
- field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
- field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
- field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
- field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
- field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
- field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
- field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
- field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
- field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
- field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
- field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
- field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
- field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
- field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
- field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
- field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
- field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
- field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
- field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
- field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
- field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
- field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
- field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
- field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
- field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
- field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN";
- field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY";
- field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
- field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
- field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
- field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
- field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
- field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
- field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
- field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
- field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
- }
-
- public static final class MediaMetadata2.Builder {
- ctor public MediaMetadata2.Builder(android.content.Context);
- ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2);
- method public android.media.MediaMetadata2 build();
- method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
- method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float);
- method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long);
- method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2);
- method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String);
- method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence);
- method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle);
- }
-
public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
@@ -24362,263 +24078,6 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
- public abstract class MediaPlayer2 extends android.media.MediaPlayerBase implements android.media.AudioRouting {
- method public abstract void attachAuxEffect(int);
- method public abstract void clearDrmEventCallback();
- method public abstract void clearMediaPlayer2EventCallback();
- method public abstract void clearPendingCommands();
- method public abstract void close();
- method public static final android.media.MediaPlayer2 create();
- method public abstract void deselectTrack(int);
- method public abstract int getAudioSessionId();
- method public abstract long getBufferedPosition();
- method public abstract long getCurrentPosition();
- method public abstract android.media.MediaPlayer2.DrmInfo getDrmInfo();
- method public abstract android.media.MediaDrm.KeyRequest getDrmKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract long getDuration();
- method public abstract android.os.PersistableBundle getMetrics();
- method public abstract android.media.PlaybackParams getPlaybackParams();
- method public abstract int getSelectedTrack(int);
- method public abstract android.media.SyncParams getSyncParams();
- method public abstract android.media.MediaTimestamp getTimestamp();
- method public abstract java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
- method public abstract int getVideoHeight();
- method public abstract int getVideoWidth();
- method public void notifyWhenCommandLabelReached(java.lang.Object);
- method public abstract void prepareDrm(java.util.UUID) throws android.media.MediaPlayer2.ProvisioningNetworkErrorException, android.media.MediaPlayer2.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
- method public abstract byte[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void releaseDrm() throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void restoreDrmKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public void seekTo(long);
- method public abstract void seekTo(long, int);
- method public abstract void selectTrack(int);
- method public abstract void setAudioSessionId(int);
- method public abstract void setAuxEffectSendLevel(float);
- method public abstract void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
- method public abstract void setDrmPropertyString(java.lang.String, java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
- method public abstract void setMediaPlayer2EventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.MediaPlayer2EventCallback);
- method public abstract void setOnDrmConfigHelper(android.media.MediaPlayer2.OnDrmConfigHelper);
- method public abstract void setPlaybackParams(android.media.PlaybackParams);
- method public abstract void setSurface(android.view.Surface);
- method public abstract void setSyncParams(android.media.SyncParams);
- field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
- field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
- field public static final int CALL_COMPLETED_LOOP_CURRENT = 3; // 0x3
- field public static final int CALL_COMPLETED_PAUSE = 4; // 0x4
- field public static final int CALL_COMPLETED_PLAY = 5; // 0x5
- field public static final int CALL_COMPLETED_PREPARE = 6; // 0x6
- field public static final int CALL_COMPLETED_RELEASE_DRM = 12; // 0xc
- field public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13; // 0xd
- field public static final int CALL_COMPLETED_SEEK_TO = 14; // 0xe
- field public static final int CALL_COMPLETED_SELECT_TRACK = 15; // 0xf
- field public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16; // 0x10
- field public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17; // 0x11
- field public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
- field public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19; // 0x13
- field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22; // 0x16
- field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23; // 0x17
- field public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24; // 0x18
- field public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25; // 0x19
- field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
- field public static final int CALL_COMPLETED_SET_SURFACE = 27; // 0x1b
- field public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28; // 0x1c
- field public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29; // 0x1d
- field public static final int CALL_STATUS_BAD_VALUE = 2; // 0x2
- field public static final int CALL_STATUS_ERROR_IO = 4; // 0x4
- field public static final int CALL_STATUS_ERROR_UNKNOWN = -2147483648; // 0x80000000
- field public static final int CALL_STATUS_INVALID_OPERATION = 1; // 0x1
- field public static final int CALL_STATUS_NO_DRM_SCHEME = 5; // 0x5
- field public static final int CALL_STATUS_NO_ERROR = 0; // 0x0
- field public static final int CALL_STATUS_PERMISSION_DENIED = 3; // 0x3
- field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
- field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
- field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
- field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
- field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
- field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
- field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
- field public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4; // 0x4
- field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
- field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
- field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
- field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
- field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
- field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
- field public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5; // 0x5
- field public static final int MEDIA_INFO_PLAYLIST_END = 6; // 0x6
- field public static final int MEDIA_INFO_PREPARED = 100; // 0x64
- field public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; // 0x2
- field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
- field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
- field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
- field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
- field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
- field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
- field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
- field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
- field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
- field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
- field public static final int SEEK_CLOSEST = 3; // 0x3
- field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
- field public static final int SEEK_NEXT_SYNC = 1; // 0x1
- field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
- field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
- }
-
- public static abstract class MediaPlayer2.DrmEventCallback {
- ctor public MediaPlayer2.DrmEventCallback();
- method public void onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
- method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int);
- }
-
- public static abstract class MediaPlayer2.DrmInfo {
- ctor public MediaPlayer2.DrmInfo();
- method public abstract java.util.Map<java.util.UUID, byte[]> getPssh();
- method public abstract java.util.List<java.util.UUID> getSupportedSchemes();
- }
-
- public static abstract class MediaPlayer2.MediaPlayer2EventCallback {
- ctor public MediaPlayer2.MediaPlayer2EventCallback();
- method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
- method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object);
- method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
- method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
- method public void onMediaTimeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp);
- method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData);
- method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
- }
-
- public static final class MediaPlayer2.MetricsConstants {
- field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
- field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
- field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
- field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
- field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
- field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
- field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
- field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
- field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
- field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
- field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
- field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
- }
-
- public static abstract class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.NoDrmSchemeException(java.lang.String);
- }
-
- public static abstract interface MediaPlayer2.OnDrmConfigHelper {
- method public abstract void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc);
- }
-
- public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.ProvisioningNetworkErrorException(java.lang.String);
- }
-
- public static abstract class MediaPlayer2.ProvisioningServerErrorException extends android.media.MediaDrmException {
- ctor protected MediaPlayer2.ProvisioningServerErrorException(java.lang.String);
- }
-
- public static abstract class MediaPlayer2.TrackInfo {
- ctor public MediaPlayer2.TrackInfo();
- method public abstract android.media.MediaFormat getFormat();
- method public abstract java.lang.String getLanguage();
- method public abstract int getTrackType();
- method public abstract java.lang.String toString();
- field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
- field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
- field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
- field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
- field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
- }
-
- public abstract class MediaPlayerBase implements java.lang.AutoCloseable {
- ctor public MediaPlayerBase();
- method public abstract android.media.AudioAttributes getAudioAttributes();
- method public long getBufferedPosition();
- method public abstract int getBufferingState();
- method public abstract android.media.DataSourceDesc getCurrentDataSource();
- method public long getCurrentPosition();
- method public long getDuration();
- method public float getMaxPlayerVolume();
- method public float getPlaybackSpeed();
- method public abstract int getPlayerState();
- method public abstract float getPlayerVolume();
- method public boolean isReversePlaybackSupported();
- method public abstract void loopCurrent(boolean);
- method public abstract void pause();
- method public abstract void play();
- method public abstract void prepare();
- method public abstract void registerPlayerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayerBase.PlayerEventCallback);
- method public abstract void reset();
- method public abstract void seekTo(long);
- method public abstract void setAudioAttributes(android.media.AudioAttributes);
- method public abstract void setDataSource(android.media.DataSourceDesc);
- method public abstract void setNextDataSource(android.media.DataSourceDesc);
- method public abstract void setNextDataSources(java.util.List<android.media.DataSourceDesc>);
- method public abstract void setPlaybackSpeed(float);
- method public abstract void setPlayerVolume(float);
- method public abstract void skipToNext();
- method public abstract void unregisterPlayerEventCallback(android.media.MediaPlayerBase.PlayerEventCallback);
- field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
- field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
- field public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3; // 0x3
- field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
- field public static final int PLAYER_STATE_ERROR = 3; // 0x3
- field public static final int PLAYER_STATE_IDLE = 0; // 0x0
- field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
- field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
- field public static final long UNKNOWN_TIME = -1L; // 0xffffffffffffffffL
- }
-
- public static abstract class MediaPlayerBase.PlayerEventCallback {
- ctor public MediaPlayerBase.PlayerEventCallback();
- method public void onBufferingStateChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc, int);
- method public void onCurrentDataSourceChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc);
- method public void onMediaPrepared(android.media.MediaPlayerBase, android.media.DataSourceDesc);
- method public void onPlayerStateChanged(android.media.MediaPlayerBase, int);
- }
-
- public abstract class MediaPlaylistAgent {
- ctor public MediaPlaylistAgent(android.content.Context);
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public int getRepeatMode();
- method public int getShuffleMode();
- method public final void notifyPlaylistChanged();
- method public final void notifyPlaylistMetadataChanged();
- method public final void notifyRepeatModeChanged();
- method public final void notifyShuffleModeChanged();
- method public final void registerPlaylistEventCallback(java.util.concurrent.Executor, android.media.MediaPlaylistAgent.PlaylistEventCallback);
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public final void unregisterPlaylistEventCallback(android.media.MediaPlaylistAgent.PlaylistEventCallback);
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- field public static final int REPEAT_MODE_ALL = 2; // 0x2
- field public static final int REPEAT_MODE_GROUP = 3; // 0x3
- field public static final int REPEAT_MODE_NONE = 0; // 0x0
- field public static final int REPEAT_MODE_ONE = 1; // 0x1
- field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
- field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
- field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
- }
-
- public static abstract class MediaPlaylistAgent.PlaylistEventCallback {
- ctor public MediaPlaylistAgent.PlaylistEventCallback();
- method public void onPlaylistChanged(android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onRepeatModeChanged(android.media.MediaPlaylistAgent, int);
- method public void onShuffleModeChanged(android.media.MediaPlaylistAgent, int);
- }
-
public class MediaRecorder implements android.media.AudioRouting {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -24895,187 +24354,6 @@
method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
}
- public class MediaSession2 implements java.lang.AutoCloseable {
- method public void addPlaylistItem(int, android.media.MediaItem2);
- method public void clearOnDataSourceMissingHelper();
- method public void close();
- method public void fastForward();
- method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
- method public android.media.MediaItem2 getCurrentMediaItem();
- method public float getPlaybackSpeed();
- method public android.media.MediaPlayerBase getPlayer();
- method public java.util.List<android.media.MediaItem2> getPlaylist();
- method public android.media.MediaPlaylistAgent getPlaylistAgent();
- method public android.media.MediaMetadata2 getPlaylistMetadata();
- method public int getRepeatMode();
- method public int getShuffleMode();
- method public android.media.SessionToken2 getToken();
- method public android.media.VolumeProvider2 getVolumeProvider();
- method public void notifyError(int, android.os.Bundle);
- method public void pause();
- method public void play();
- method public void prepare();
- method public void removePlaylistItem(android.media.MediaItem2);
- method public void replacePlaylistItem(int, android.media.MediaItem2);
- method public void rewind();
- method public void seekTo(long);
- method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
- method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
- method public void setAudioFocusRequest(android.media.AudioFocusRequest);
- method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
- method public void setOnDataSourceMissingHelper(android.media.MediaSession2.OnDataSourceMissingHelper);
- method public void setPlaybackSpeed(float);
- method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void setRepeatMode(int);
- method public void setShuffleMode(int);
- method public void skipToNextItem();
- method public void skipToPlaylistItem(android.media.MediaItem2);
- method public void skipToPreviousItem();
- method public void stop();
- method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistAgent, android.media.VolumeProvider2);
- method public void updatePlaylistMetadata(android.media.MediaMetadata2);
- field public static final int COMMAND_CODE_BROWSER = 28; // 0x1c
- field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
- field public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11; // 0xb
- field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
- field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2
- field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1
- field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
- field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
- field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
- field public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10; // 0xa
- field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
- field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
- field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
- field public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15; // 0xf
- field public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18; // 0x12
- field public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20; // 0x14
- field public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16; // 0x10
- field public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17; // 0x11
- field public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19; // 0x13
- field public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21; // 0x15
- field public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14; // 0xe
- field public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13; // 0xd
- field public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12; // 0xc
- field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 22; // 0x16
- field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 24; // 0x18
- field public static final int COMMAND_CODE_PLAY_FROM_URI = 23; // 0x17
- field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 25; // 0x19
- field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 27; // 0x1b
- field public static final int COMMAND_CODE_PREPARE_FROM_URI = 26; // 0x1a
- field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
- field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
- field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
- field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
- field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
- field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
- field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
- field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
- field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
- field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
- field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc
- field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
- field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
- }
-
- public static final class MediaSession2.Builder {
- ctor public MediaSession2.Builder(android.content.Context);
- method public android.media.MediaSession2 build();
- method public android.media.MediaSession2.Builder setId(java.lang.String);
- method public android.media.MediaSession2.Builder setPlayer(android.media.MediaPlayerBase);
- method public android.media.MediaSession2.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
- method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
- method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
- method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
- }
-
- public static final class MediaSession2.Command {
- ctor public MediaSession2.Command(android.content.Context, int);
- ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
- method public int getCommandCode();
- method public java.lang.String getCustomCommand();
- method public android.os.Bundle getExtras();
- }
-
- public static final class MediaSession2.CommandButton {
- method public android.media.MediaSession2.Command getCommand();
- method public java.lang.String getDisplayName();
- method public android.os.Bundle getExtras();
- method public int getIconResId();
- method public boolean isEnabled();
- }
-
- public static final class MediaSession2.CommandButton.Builder {
- ctor public MediaSession2.CommandButton.Builder(android.content.Context);
- method public android.media.MediaSession2.CommandButton build();
- method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
- method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
- method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
- method public android.media.MediaSession2.CommandButton.Builder setExtras(android.os.Bundle);
- method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
- }
-
- public static final class MediaSession2.CommandGroup {
- ctor public MediaSession2.CommandGroup(android.content.Context);
- ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
- method public void addAllPredefinedCommands();
- method public void addCommand(android.media.MediaSession2.Command);
- method public java.util.List<android.media.MediaSession2.Command> getCommands();
- method public boolean hasCommand(android.media.MediaSession2.Command);
- method public boolean hasCommand(int);
- method public void removeCommand(android.media.MediaSession2.Command);
- }
-
- public static final class MediaSession2.ControllerInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- method public boolean isTrusted();
- }
-
- public static abstract interface MediaSession2.OnDataSourceMissingHelper {
- method public abstract android.media.DataSourceDesc onDataSourceMissing(android.media.MediaSession2, android.media.MediaItem2);
- }
-
- public static abstract class MediaSession2.SessionCallback {
- ctor public MediaSession2.SessionCallback(android.content.Context);
- method public void onBufferingStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2, int);
- method public boolean onCommandRequest(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
- method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
- method public void onCurrentMediaItemChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
- method public void onCustomCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
- method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
- method public void onMediaPrepared(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
- method public void onPlayFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPlayFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPlayFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
- method public void onPlayerStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, int);
- method public void onPlaylistChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
- method public void onPlaylistMetadataChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
- method public void onPrepareFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPrepareFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
- method public void onPrepareFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
- method public void onRepeatModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
- method public void onSetRating(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
- method public void onShuffleModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
- }
-
- public abstract class MediaSessionService2 extends android.app.Service {
- ctor public MediaSessionService2();
- method public final android.media.MediaSession2 getSession();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.MediaSession2 onCreateSession(java.lang.String);
- method public android.media.MediaSessionService2.MediaNotification onUpdateNotification();
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2";
- field public static final java.lang.String SERVICE_META_DATA = "android.media.session";
- }
-
- public static class MediaSessionService2.MediaNotification {
- ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification);
- method public android.app.Notification getNotification();
- method public int getNotificationId();
- }
-
public final class MediaSync {
ctor public MediaSync();
method public android.view.Surface createInputSurface();
@@ -25368,22 +24646,6 @@
field public static final int URI_COLUMN_INDEX = 2; // 0x2
}
- public final class SessionToken2 {
- ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String);
- method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle);
- method public java.lang.String getId();
- method public java.lang.String getPackageName();
- method public int getType();
- method public int getUid();
- method public android.os.Bundle toBundle();
- field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
- field public static final int TYPE_SESSION = 0; // 0x0
- field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
- }
-
- public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation {
- }
-
public class SoundPool {
ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
@@ -25587,19 +24849,6 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
- public abstract class VolumeProvider2 {
- ctor public VolumeProvider2(android.content.Context, int, int, int);
- method public final int getControlType();
- method public final int getCurrentVolume();
- method public final int getMaxVolume();
- method public void onAdjustVolume(int);
- method public void onSetVolumeTo(int);
- method public final void setCurrentVolume(int);
- field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
- field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
- field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
- }
-
public final class VolumeShaper implements java.lang.AutoCloseable {
method public void apply(android.media.VolumeShaper.Operation);
method public void close();
@@ -26542,23 +25791,14 @@
public final class MediaSessionManager {
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
- method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
- method public java.util.List<android.media.SessionToken2> getActiveSessionTokens();
method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
- method public java.util.List<android.media.SessionToken2> getAllSessionTokens();
- method public java.util.List<android.media.SessionToken2> getSessionServiceTokens();
method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
- method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
}
public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
}
- public static abstract interface MediaSessionManager.OnSessionTokensChangedListener {
- method public abstract void onSessionTokensChanged(java.util.List<android.media.SessionToken2>);
- }
-
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
@@ -41421,7 +40661,7 @@
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
field public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2; // 0x2
- field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4; // 0x4
+ field public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4; // 0x4
field public static final int HANDOVER_FAILURE_UNKNOWN = 5; // 0x5
field public static final int HANDOVER_FAILURE_USER_REJECTED = 3; // 0x3
}
@@ -44057,7 +43297,7 @@
method public abstract int getSpanTypeId();
}
- public class PrecomputedText implements android.text.Spanned {
+ public class PrecomputedText implements android.text.Spannable {
method public char charAt(int);
method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
method public int getParagraphCount();
@@ -44071,6 +43311,8 @@
method public java.lang.CharSequence getText();
method public int length();
method public int nextSpanTransition(int, int, java.lang.Class);
+ method public void removeSpan(java.lang.Object);
+ method public void setSpan(java.lang.Object, int, int, int);
method public java.lang.CharSequence subSequence(int, int);
}
@@ -53433,20 +52675,6 @@
method public void update();
}
- public class MediaControlView2 extends android.view.ViewGroup {
- ctor public MediaControlView2(android.content.Context);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int);
- ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int, int);
- method public void requestPlayButtonFocus();
- method public void setMediaSessionToken(android.media.SessionToken2);
- method public void setOnFullScreenListener(android.widget.MediaControlView2.OnFullScreenListener);
- }
-
- public static abstract interface MediaControlView2.OnFullScreenListener {
- method public abstract void onFullScreen(android.view.View, boolean);
- }
-
public class MediaController extends android.widget.FrameLayout {
ctor public MediaController(android.content.Context, android.util.AttributeSet);
ctor public MediaController(android.content.Context, boolean);
@@ -54837,27 +54065,6 @@
method public void suspend();
}
- public class VideoView2 extends android.view.ViewGroup {
- ctor public VideoView2(android.content.Context);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet, int);
- ctor public VideoView2(android.content.Context, android.util.AttributeSet, int, int);
- method public android.widget.MediaControlView2 getMediaControlView2();
- method public android.media.SessionToken2 getMediaSessionToken();
- method public int getViewType();
- method public boolean isSubtitleEnabled();
- method public void setAudioAttributes(android.media.AudioAttributes);
- method public void setAudioFocusRequest(int);
- method public void setDataSource(android.media.DataSourceDesc);
- method public void setMediaControlView2(android.widget.MediaControlView2, long);
- method public void setMediaItem(android.media.MediaItem2);
- method public void setSpeed(float);
- method public void setSubtitleEnabled(boolean);
- method public void setViewType(int);
- field public static final int VIEW_TYPE_SURFACEVIEW = 1; // 0x1
- field public static final int VIEW_TYPE_TEXTUREVIEW = 2; // 0x2
- }
-
public class ViewAnimator extends android.widget.FrameLayout {
ctor public ViewAnimator(android.content.Context);
ctor public ViewAnimator(android.content.Context, android.util.AttributeSet);
diff --git a/api/removed.txt b/api/removed.txt
index a5370f4..1228fd1 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -155,6 +155,13 @@
public final class ImageDecoder implements java.lang.AutoCloseable {
method public deprecated boolean getAsAlphaMask();
method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+ field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
+ field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
+ field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
+ }
+
+ public static deprecated class ImageDecoder.IncompleteException extends java.io.IOException {
+ ctor public ImageDecoder.IncompleteException();
}
public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
diff --git a/api/system-current.txt b/api/system-current.txt
index a056115..d602b56 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -100,6 +100,7 @@
field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+ field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -181,6 +182,7 @@
field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
+ field public static final java.lang.String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -207,7 +209,7 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
- field public static final int userRestriction = 16844165; // 0x1010585
+ field public static final int userRestriction = 16844164; // 0x1010584
}
public static final class R.raw {
@@ -283,6 +285,7 @@
field public static final java.lang.String OPSTR_GET_ACCOUNTS = "android:get_accounts";
field public static final java.lang.String OPSTR_GPS = "android:gps";
field public static final java.lang.String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+ field public static final java.lang.String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
field public static final java.lang.String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final java.lang.String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
field public static final java.lang.String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -1016,15 +1019,19 @@
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public android.os.PersistableBundle getSuspendedPackageAppExtras(java.lang.String);
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public boolean isPackageSuspended(java.lang.String);
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
+ method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+ method public void setSuspendedPackageAppExtras(java.lang.String, android.os.PersistableBundle);
method public abstract void setUpdateAvailable(java.lang.String, boolean);
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -4311,7 +4318,6 @@
method public int getUserSecretType();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainProtectionParams> CREATOR;
- field public static final int TYPE_CUSTOM_PASSWORD = 101; // 0x65
field public static final int TYPE_LOCKSCREEN = 100; // 0x64
field public static final int UI_FORMAT_PASSWORD = 2; // 0x2
field public static final int UI_FORMAT_PATTERN = 3; // 0x3
@@ -4343,11 +4349,14 @@
}
public final class KeyDerivationParams implements android.os.Parcelable {
+ method public static android.security.keystore.recovery.KeyDerivationParams createScryptParams(byte[], int);
method public static android.security.keystore.recovery.KeyDerivationParams createSha256Params(byte[]);
method public int describeContents();
method public int getAlgorithm();
+ method public int getMemoryDifficulty();
method public byte[] getSalt();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ALGORITHM_SCRYPT = 2; // 0x2
field public static final int ALGORITHM_SHA256 = 1; // 0x1
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyDerivationParams> CREATOR;
}
@@ -4366,7 +4375,6 @@
method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
method public android.security.keystore.recovery.KeyChainSnapshot getKeyChainSnapshot() throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -4375,7 +4383,6 @@
method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
- method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 31e3e7c..dd16771 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -79,6 +79,7 @@
field public static final java.lang.String OPSTR_GET_ACCOUNTS = "android:get_accounts";
field public static final java.lang.String OPSTR_GPS = "android:gps";
field public static final java.lang.String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+ field public static final java.lang.String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
field public static final java.lang.String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final java.lang.String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
field public static final java.lang.String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -294,6 +295,14 @@
}
+package android.graphics {
+
+ public final class ImageDecoder implements java.lang.AutoCloseable {
+ method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
+ }
+
+}
+
package android.graphics.drawable {
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -407,20 +416,20 @@
ctor public GnssMeasurement();
method public void reset();
method public void resetAutomaticGainControlLevel();
- method public void resetCarrierCycles();
+ method public deprecated void resetCarrierCycles();
method public void resetCarrierFrequencyHz();
- method public void resetCarrierPhase();
- method public void resetCarrierPhaseUncertainty();
+ method public deprecated void resetCarrierPhase();
+ method public deprecated void resetCarrierPhaseUncertainty();
method public void resetSnrInDb();
method public void set(android.location.GnssMeasurement);
method public void setAccumulatedDeltaRangeMeters(double);
method public void setAccumulatedDeltaRangeState(int);
method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
method public void setAutomaticGainControlLevelInDb(double);
- method public void setCarrierCycles(long);
+ method public deprecated void setCarrierCycles(long);
method public void setCarrierFrequencyHz(float);
- method public void setCarrierPhase(double);
- method public void setCarrierPhaseUncertainty(double);
+ method public deprecated void setCarrierPhase(double);
+ method public deprecated void setCarrierPhaseUncertainty(double);
method public void setCn0DbHz(double);
method public void setConstellationType(int);
method public void setMultipathIndicator(int);
@@ -432,6 +441,7 @@
method public void setState(int);
method public void setSvid(int);
method public void setTimeOffsetNanos(double);
+ field public static final int ADR_STATE_ALL = 31; // 0x1f
}
public final class GnssMeasurementsEvent implements android.os.Parcelable {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 525ddb3..79c0d71 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -138,7 +138,7 @@
# Enable sanitizer on eng builds
ifeq ($(TARGET_BUILD_VARIANT),eng)
LOCAL_CLANG := true
- LOCAL_SANITIZE := address unsigned-integer-overflow signed-integer-overflow
+ LOCAL_SANITIZE := address
endif
LOCAL_INIT_RC := statsd.rc
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 82274a6..a35570b 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -360,7 +360,7 @@
metricsManager.dropData(timestampNs);
StatsdStats::getInstance().noteDataDropped(key);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
- } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
+ } else if (totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) {
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index d85157c..79067eb 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -68,10 +68,10 @@
if (itr->second != nullptr && timestampNs >= NS_PER_SEC * itr->second->timestampSec) {
declareAnomaly(timestampNs, dimensionKey);
}
- mAlarms.erase(dimensionKey);
if (mAlarmMonitor != nullptr) {
mAlarmMonitor->remove(itr->second);
}
+ mAlarms.erase(dimensionKey);
}
void DurationAnomalyTracker::cancelAllAlarms() {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index c42514a..a4f64dd 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -96,9 +96,13 @@
const static int kMaxLogSourceCount = 50;
- // Max memory allowed for storing metrics per configuration. When this limit is approached,
- // statsd will send a broadcast so that the client can fetch the data and clear this memory.
- static const size_t kMaxMetricsBytesPerConfig = 128 * 1024;
+ // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
+ // drops the metrics data in memory.
+ static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+
+ // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
+ // data subscriber that it's time to call getData.
+ static const size_t kBytesPerConfigTriggerGetData = 128 * 1024;
// Cap the UID map's memory usage to this. This should be fairly high since the UID information
// is critical for understanding the metrics.
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 335ec4c..c9547cf 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -205,11 +205,12 @@
bool hasPendingEvent =
false; // has either a kStarted or kPaused event across bucket boundaries
// meaning we need to carry them over to the new bucket.
- for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
+ for (auto it = mInfos.begin(); it != mInfos.end();) {
if (it->second.state == DurationState::kStopped) {
// No need to keep buckets for events that were stopped before.
- mInfos.erase(it);
+ it = mInfos.erase(it);
} else {
+ ++it;
hasPendingEvent = true;
}
}
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 3ba4b7a..1cb20bc 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -365,7 +365,8 @@
count++;
proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
(long long)record.timestampNs);
- proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data());
+ proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data(),
+ record.bytes.size());
proto->end(snapshotsToken);
}
}
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3d8aa47..643d2bf 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -167,8 +167,7 @@
return;
}
- const char* suffix =
- StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+ string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
dirent* de;
while ((de = readdir(dir.get()))) {
@@ -176,9 +175,9 @@
if (name[0] == '.') continue;
size_t nameLen = strlen(name);
- size_t suffixLen = strlen(suffix);
+ size_t suffixLen = suffix.length();
if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+ strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
int64_t result[3];
parseFileName(name, result);
if (result[0] == -1) continue;
@@ -262,8 +261,7 @@
return false;
}
- const char* suffix =
- StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+ string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
dirent* de;
while ((de = readdir(dir.get()))) {
@@ -272,10 +270,10 @@
continue;
}
size_t nameLen = strlen(name);
- size_t suffixLen = strlen(suffix);
+ size_t suffixLen = suffix.length();
// There can be at most one file that matches this suffix (config key).
if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+ strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
O_RDONLY | O_CLOEXEC);
if (fd != -1) {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index e4df85b..9cba926 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -669,7 +669,6 @@
Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
Landroid/graphics/GraphicBuffer;->mNativeObject:J
Landroid/graphics/ImageDecoder;-><init>(JIIZ)V
-Landroid/graphics/ImageDecoder;->onPartialImage(I)Z
Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
Landroid/graphics/LinearGradient;->mColors:[I
Landroid/graphics/Matrix;->native_instance:J
@@ -977,6 +976,7 @@
Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getData()[B
Landroid/media/soundtrigger/SoundTriggerManager;->loadSoundModel(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;)I
Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/app/PendingIntent;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
+Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/os/Bundle;Landroid/content/ComponentName;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
@@ -1158,6 +1158,7 @@
Landroid/os/Binder;->mObject:J
Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
Landroid/os/Build;->IS_DEBUGGABLE:Z
+Landroid/os/Build;->IS_EMULATOR:Z
Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4690211..ea8c71c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -270,8 +270,10 @@
public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
/** @hide Continue handover of a call from another app */
public static final int OP_ACCEPT_HANDOVER = 74;
+ /** @hide Create and Manage IPsec Tunnels */
+ public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
/** @hide */
- public static final int _NUM_OP = 75;
+ public static final int _NUM_OP = 76;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -507,6 +509,9 @@
@SystemApi @TestApi
public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE =
"android:bind_accessibility_service";
+ /** @hide */
+ @SystemApi @TestApi
+ public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -641,6 +646,7 @@
OP_REQUEST_DELETE_PACKAGES,
OP_BIND_ACCESSIBILITY_SERVICE,
OP_ACCEPT_HANDOVER,
+ OP_MANAGE_IPSEC_TUNNELS,
};
/**
@@ -722,6 +728,7 @@
OPSTR_REQUEST_DELETE_PACKAGES,
OPSTR_BIND_ACCESSIBILITY_SERVICE,
OPSTR_ACCEPT_HANDOVER,
+ OPSTR_MANAGE_IPSEC_TUNNELS,
};
/**
@@ -804,6 +811,7 @@
"REQUEST_DELETE_PACKAGES",
"BIND_ACCESSIBILITY_SERVICE",
"ACCEPT_HANDOVER",
+ "MANAGE_IPSEC_TUNNELS",
};
/**
@@ -886,6 +894,7 @@
Manifest.permission.REQUEST_DELETE_PACKAGES,
Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
Manifest.permission.ACCEPT_HANDOVER,
+ null, // no permission for OP_MANAGE_IPSEC_TUNNELS
};
/**
@@ -969,6 +978,7 @@
null, // REQUEST_DELETE_PACKAGES
null, // OP_BIND_ACCESSIBILITY_SERVICE
null, // ACCEPT_HANDOVER
+ null, // MANAGE_IPSEC_TUNNELS
};
/**
@@ -1051,6 +1061,7 @@
false, // OP_REQUEST_DELETE_PACKAGES
false, // OP_BIND_ACCESSIBILITY_SERVICE
false, // ACCEPT_HANDOVER
+ false, // MANAGE_IPSEC_HANDOVERS
};
/**
@@ -1132,6 +1143,7 @@
AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES
AppOpsManager.MODE_ALLOWED, // OP_BIND_ACCESSIBILITY_SERVICE
AppOpsManager.MODE_ALLOWED, // ACCEPT_HANDOVER
+ AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
};
/**
@@ -1217,6 +1229,7 @@
false, // OP_REQUEST_DELETE_PACKAGES
false, // OP_BIND_ACCESSIBILITY_SERVICE
false, // ACCEPT_HANDOVER
+ false, // MANAGE_IPSEC_TUNNELS
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b8c4ef7..fb8ded1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -70,6 +70,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -2151,16 +2152,42 @@
}
@Override
- public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
- int userId) {
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras,
+ String dialogMessage) {
+ // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
try {
- return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
+ return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
+ launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
+ public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+ try {
+ return mPM.getPackageSuspendedAppExtras(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public PersistableBundle getSuspendedPackageAppExtras() {
+ return getSuspendedPackageAppExtras(mContext.getOpPackageName());
+ }
+
+ @Override
+ public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
+ try {
+ mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
try {
return mPM.isPackageSuspendedForUser(packageName, userId);
@@ -2171,6 +2198,17 @@
/** @hide */
@Override
+ public boolean isPackageSuspended(String packageName) {
+ return isPackageSuspendedForUser(packageName, mContext.getUserId());
+ }
+
+ @Override
+ public boolean isPackageSuspended() {
+ return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
+ }
+
+ /** @hide */
+ @Override
public void setApplicationCategoryHint(String packageName, int categoryHint) {
try {
mPM.setApplicationCategoryHint(packageName, categoryHint,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5d21be5..d3c1e99 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3118,7 +3118,6 @@
private int mActionBarColor = COLOR_INVALID;
private int mBackgroundColor = COLOR_INVALID;
private int mForegroundColor = COLOR_INVALID;
- private int mBackgroundColorHint = COLOR_INVALID;
/**
* A temporary location where actions are stored. If != null the view originally has action
* but doesn't have any for this inflation.
@@ -4387,8 +4386,7 @@
backgroundColor);
mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
backgroundColor);
- if (backgroundColor != COLOR_DEFAULT
- && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
+ if (backgroundColor != COLOR_DEFAULT && isColorized()) {
mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
mPrimaryTextColor, backgroundColor, 4.5);
mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
@@ -4595,21 +4593,13 @@
}
private void bindExpandButton(RemoteViews contentView) {
- int color = getPrimaryHighlightColor();
+ int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
contentView.setDrawableTint(R.id.expand_button, false, color,
PorterDuff.Mode.SRC_ATOP);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
- /**
- * @return the color that is used as the first primary highlight color. This is applied
- * in several places like the action buttons or the app name in the header.
- */
- private int getPrimaryHighlightColor() {
- return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
- }
-
private void bindHeaderChronometerAndTime(RemoteViews contentView) {
if (showsTimeOrChronometer()) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
@@ -4706,7 +4696,7 @@
setTextViewColorPrimary(contentView, R.id.app_name_text);
} else {
contentView.setTextColor(R.id.app_name_text,
- ambient ? resolveAmbientColor() : resolveContrastColor());
+ ambient ? resolveAmbientColor() : getSecondaryTextColor());
}
}
@@ -5234,7 +5224,14 @@
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
boolean ambient) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
- int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
+ int color;
+ if (ambient) {
+ color = resolveAmbientColor();
+ } else if (isColorized()) {
+ color = getPrimaryTextColor();
+ } else {
+ color = resolveContrastColor();
+ }
if (colorable) {
contentView.setDrawableTint(R.id.icon, false, color,
PorterDuff.Mode.SRC_ATOP);
@@ -5270,14 +5267,11 @@
}
int color;
- int background = mBackgroundColorHint;
- if (mBackgroundColorHint == COLOR_INVALID) {
- background = mContext.getColor(
- com.android.internal.R.color.notification_material_background_color);
- }
+ int background = mContext.getColor(
+ com.android.internal.R.color.notification_material_background_color);
if (mN.color == COLOR_DEFAULT) {
ensureColors();
- color = mSecondaryTextColor;
+ color = NotificationColorUtil.resolveDefaultColor(mContext, background);
} else {
color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
background, mInNightMode);
@@ -5517,8 +5511,7 @@
if (isColorized()) {
return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
} else {
- return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
- : COLOR_DEFAULT;
+ return COLOR_DEFAULT;
}
}
@@ -5555,18 +5548,6 @@
}
/**
- * Sets the background color for this notification to be a different one then the default.
- * This is mainly used to calculate contrast and won't necessarily be applied to the
- * background.
- *
- * @hide
- */
- public void setBackgroundColorHint(int backgroundColor) {
- mBackgroundColorHint = backgroundColor;
- }
-
-
- /**
* Forces all styled remoteViews to be built from scratch and not use any cached
* RemoteViews.
* This is needed for legacy apps that are baking in their remoteviews into the
@@ -5972,7 +5953,7 @@
* @hide
*/
public abstract boolean areNotificationsVisiblyDifferent(Style other);
-
+
/**
* @return the the text that should be displayed in the statusBar when heads-upped.
* If {@code null} is returned, the default implementation will be used.
@@ -7498,8 +7479,7 @@
}
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- final RemoteViews button = generateMediaActionButton(action,
- getPrimaryHighlightColor());
+ final RemoteViews button = generateMediaActionButton(action, getActionColor());
view.addView(com.android.internal.R.id.media_actions, button);
}
}
@@ -7513,8 +7493,9 @@
return view;
}
- private int getPrimaryHighlightColor() {
- return mBuilder.getPrimaryHighlightColor();
+ private int getActionColor() {
+ return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
+ : mBuilder.resolveContrastColor();
}
private RemoteViews makeMediaBigContentView() {
@@ -7534,7 +7515,7 @@
big.removeAllViews(com.android.internal.R.id.media_actions);
for (int i = 0; i < actionCount; i++) {
final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
- getPrimaryHighlightColor());
+ getActionColor());
big.addView(com.android.internal.R.id.media_actions, button);
}
}
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index b7100e6..6feb38e 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.IntDef;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
@@ -25,6 +26,8 @@
import android.os.Parcelable;
import android.util.ArraySet;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -74,9 +77,14 @@
private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
"android.remoteinput.dataTypeResultsData";
- /** Extra added to a clip data intent object identifying the source of the results. */
+ /** Extra added to a clip data intent object identifying the {@link Source} of the results. */
private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource";
+ /** @hide */
+ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_FREE_FORM_INPUT, SOURCE_CHOICE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
/** The user manually entered the data. */
public static final int SOURCE_FREE_FORM_INPUT = 0;
@@ -437,10 +445,9 @@
*
* @param intent The intent to add remote input source to. The {@link ClipData}
* field of the intent will be modified to contain the source.
- * field of the intent will be modified to contain the source.
* @param source The source of the results.
*/
- public static void setResultsSource(Intent intent, int source) {
+ public static void setResultsSource(Intent intent, @Source int source) {
Intent clipDataIntent = getClipDataIntentFromIntent(intent);
if (clipDataIntent == null) {
clipDataIntent = new Intent(); // First time we've added a result.
@@ -460,6 +467,7 @@
* @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will
* be returned.
*/
+ @Source
public static int getResultsSource(Intent intent) {
Intent clipDataIntent = getClipDataIntentFromIntent(intent);
if (clipDataIntent == null) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 36a74a4..f4352f9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,8 +50,8 @@
import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.content.IntentSender;
/**
@@ -272,9 +272,17 @@
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
- String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, int userId);
+ String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
+ in PersistableBundle launcherExtras, in PersistableBundle appExtras,
+ String callingPackage, int userId);
+
boolean isPackageSuspendedForUser(String packageName, int userId);
+ PersistableBundle getPackageSuspendedAppExtras(String pacakgeName, int userId);
+
+ void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
+ int userId);
+
/**
* Backup/restore support - only the system uid may use these.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3536eea..4d8773c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -51,6 +51,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -5510,28 +5511,49 @@
/**
* Puts the package in a suspended state, where attempts at starting activities are denied.
*
- * <p>It doesn't remove the data or the actual package file. The application notifications
- * will be hidden, the application will not show up in recents, will not be able to show
- * toasts or dialogs or ring the device.
+ * <p>It doesn't remove the data or the actual package file. The application's notifications
+ * will be hidden, any of the it's started activities will be stopped and it will not be able to
+ * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
+ * system dialog with the given {@code dialogMessage} will be shown instead.</p>
*
* <p>The package must already be installed. If the package is uninstalled while suspended
- * the package will no longer be suspended.
+ * the package will no longer be suspended. </p>
+ *
+ * <p>Optionally, the suspending app can provide extra information in the form of
+ * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+ * launcher to support customization that they might need to handle the suspended state. </p>
+ *
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
+ * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true} than the packages will be suspended, if set to
- * {@code false} the packages will be unsuspended.
- * @param userId The user id.
+ * {@code false}, the packages will be unsuspended.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogMessage The message to be displayed to the user, when they try to launch a
+ * suspended app.
*
* @return an array of package names for which the suspended status is not set as requested in
* this method.
*
* @hide
*/
- public abstract String[] setPackagesSuspendedAsUser(
- String[] packageNames, boolean suspended, @UserIdInt int userId);
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.SUSPEND_APPS,
+ Manifest.permission.MANAGE_USERS})
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ String dialogMessage) {
+ throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+ }
/**
- * @see #setPackageSuspendedAsUser(String, boolean, int)
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
* @param packageName The name of the package to get the suspended status of.
* @param userId The user id.
* @return {@code true} if the package is suspended or {@code false} if the package is not
@@ -5541,6 +5563,86 @@
public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
/**
+ * Query if an app is currently suspended.
+ *
+ * @return {@code true} if the given package is suspended, {@code false} otherwise
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ * @hide
+ */
+ @SystemApi
+ public boolean isPackageSuspended(String packageName) {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Apps can query this to know if they have been suspended.
+ *
+ * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+ *
+ * @see #getSuspendedPackageAppExtras()
+ */
+ public boolean isPackageSuspended() {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
+ * package was suspended.
+ *
+ * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
+ * api.</p>
+ *
+ * @param packageName The package to retrieve extras for.
+ * @return The {@code appExtras} for the suspended package.
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+ throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
+ * Set the app extras for a suspended package. This method can be used to update the appExtras
+ * for a package that was earlier suspended using
+ * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * String)}
+ * Does nothing if the given package is not already in a suspended state.
+ *
+ * @param packageName The package for which the appExtras need to be updated
+ * @param appExtras The new appExtras for the given package
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ public void setSuspendedPackageAppExtras(String packageName,
+ @Nullable PersistableBundle appExtras) {
+ throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
+ * Returns any extra information supplied as {@code appExtras} to the system when the calling
+ * app was suspended.
+ *
+ * <p> Note: This just returns whatever {@link PersistableBundle} was passed to the system via
+ * {@code setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * String)} when suspending the package, <em> which might be {@code null}. </em></p>
+ *
+ * @return A {@link PersistableBundle} containing the extras for the app, or {@code null} if the
+ * package is not currently suspended.
+ * @see #isPackageSuspended()
+ */
+ public @Nullable PersistableBundle getSuspendedPackageAppExtras() {
+ throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
* <p>
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 293beb2..f7b6e09 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -27,6 +27,8 @@
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
@@ -44,6 +46,9 @@
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
+ public String suspendingPackage;
+ public PersistableBundle suspendedAppExtras;
+ public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
public boolean virtualPreload;
public int enabled;
@@ -76,6 +81,9 @@
notLaunched = o.notLaunched;
hidden = o.hidden;
suspended = o.suspended;
+ suspendingPackage = o.suspendingPackage;
+ suspendedAppExtras = o.suspendedAppExtras;
+ suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
virtualPreload = o.virtualPreload;
enabled = o.enabled;
@@ -195,6 +203,20 @@
if (suspended != oldState.suspended) {
return false;
}
+ if (suspended) {
+ if (suspendingPackage == null
+ || !suspendingPackage.equals(oldState.suspendingPackage)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedAppExtras,
+ oldState.suspendedAppExtras)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
+ oldState.suspendedLauncherExtras)) {
+ return false;
+ }
+ }
if (instantApp != oldState.instantApp) {
return false;
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 72db33f..f47d464 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -819,7 +819,8 @@
* @param config A session configuration (see {@link SessionConfiguration}).
*
* @throws IllegalArgumentException In case the session configuration is invalid; or the output
- * configurations are empty.
+ * configurations are empty; or the session configuration
+ * executor is invalid.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a2bc91e..4d64295 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -25,6 +25,7 @@
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
+import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -41,6 +42,11 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
/**
* <p>A system service manager for detecting, characterizing, and connecting to
@@ -123,16 +129,8 @@
*/
public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
@Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
-
- CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
+ CameraManagerGlobal.get().registerAvailabilityCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
}
/**
@@ -170,15 +168,8 @@
* no looper.
*/
public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
- CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+ CameraManagerGlobal.get().registerTorchCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
}
/**
@@ -728,12 +719,13 @@
*/
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
- // Registered availablility callbacks and their handlers
- private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
- new ArrayMap<AvailabilityCallback, Handler>();
+ // Registered availablility callbacks and their executors
+ private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
+ new ArrayMap<AvailabilityCallback, Executor>();
// torch client binder to set the torch mode with.
private Binder mTorchClientBinder = new Binder();
@@ -741,9 +733,9 @@
// Camera ID -> Torch status map
private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
- // Registered torch callbacks and their handlers
- private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
- new ArrayMap<TorchCallback, Handler>();
+ // Registered torch callbacks and their executors
+ private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
+ new ArrayMap<TorchCallback, Executor>();
private final Object mLock = new Object();
@@ -925,49 +917,63 @@
}
}
- private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
+ private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
final String id, final int status) {
if (isAvailable(status)) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraAvailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraAvailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraUnavailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraUnavailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
- private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+ private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
final String id, final int status) {
switch(status) {
case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
- case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeChanged(id, status ==
- ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
- }
+ case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeChanged(id, status ==
+ ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
- default:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeUnavailable(id);
- }
+ default: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeUnavailable(id);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
}
}
@@ -976,11 +982,11 @@
* Send the state of all known cameras to the provided listener, to initialize
* the listener's knowledge of camera state.
*/
- private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+ private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
}
@@ -1039,18 +1045,18 @@
final int callbackCount = mCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- Handler handler = mCallbackMap.valueAt(i);
+ Executor executor = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
} // onStatusChangedLocked
- private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+ private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
for (int i = 0; i < mTorchStatus.size(); i++) {
String id = mTorchStatus.keyAt(i);
Integer status = mTorchStatus.valueAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
}
@@ -1078,9 +1084,9 @@
final int callbackCount = mTorchCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- final Handler handler = mTorchCallbackMap.valueAt(i);
+ final Executor executor = mTorchCallbackMap.valueAt(i);
final TorchCallback callback = mTorchCallbackMap.keyAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
} // onTorchStatusChangedLocked
@@ -1089,16 +1095,16 @@
* global listener singleton.
*
* @param callback the new callback to send camera availability notices to
- * @param handler The handler on which the callback should be invoked. May not be null.
+ * @param executor The executor which should invoke the callback. May not be null.
*/
- public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
synchronized (mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mCallbackMap.put(callback, handler);
+ Executor oldExecutor = mCallbackMap.put(callback, executor);
// For new callbacks, provide initial availability information
- if (oldHandler == null) {
- updateCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1120,14 +1126,14 @@
}
}
- public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ public void registerTorchCallback(TorchCallback callback, Executor executor) {
synchronized(mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+ Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
// For new callbacks, provide initial torch information
- if (oldHandler == null) {
- updateTorchCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateTorchCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1165,13 +1171,7 @@
* availability callback or torch status callback.
*/
private void scheduleCameraServiceReconnectionLocked() {
- final Handler handler;
-
- if (mCallbackMap.size() > 0) {
- handler = mCallbackMap.valueAt(0);
- } else if (mTorchCallbackMap.size() > 0) {
- handler = mTorchCallbackMap.valueAt(0);
- } else {
+ if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
// Not necessary to reconnect camera service if no client registers a callback.
return;
}
@@ -1181,22 +1181,21 @@
" ms");
}
- handler.postDelayed(
- new Runnable() {
- @Override
- public void run() {
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Reconnecting Camera Service failed.");
- }
- scheduleCameraServiceReconnectionLocked();
- }
+ try {
+ mScheduler.schedule(() -> {
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.v(TAG, "Reconnecting Camera Service failed.");
}
+ scheduleCameraServiceReconnectionLocked();
}
- },
- CAMERA_SERVICE_RECONNECT_DELAY_MS);
+ }
+ }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
deleted file mode 100644
index 866f370..0000000
--- a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * A dispatcher that replaces one argument with another; replaces any argument at an index
- * with another argument.
- *
- * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
- * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
- * be something
- * that's not an {@code int}.</p>
- *
- * @param <T>
- * source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <TArg>
- * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
- * will be overriden to objects of this type
- */
-public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
-
- private final Dispatchable<T> mTarget;
- private final int mArgumentIndex;
- private final TArg mReplaceWith;
-
- /**
- * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
- * after the argument is replaced.
- *
- * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
- * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
- * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
- *
- * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
- * passed through with the arguments unchanged.</p>
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param argumentIndex the numeric index of the argument {@code >= 0}
- * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
- */
- public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
- TArg replaceWith) {
- mTarget = checkNotNull(target, "target must not be null");
- mArgumentIndex = checkArgumentNonnegative(argumentIndex,
- "argumentIndex must not be negative");
- mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
-
- if (args.length > mArgumentIndex) {
- args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
- args[mArgumentIndex] = mReplaceWith;
- }
-
- return mTarget.dispatch(method, args);
- }
-
- private static Object[] arrayCopy(Object[] array) {
- int length = array.length;
- Object[] newArray = new Object[length];
- for (int i = 0; i < length; ++i) {
- newArray[i] = array[i];
- }
- return newArray;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
deleted file mode 100644
index fe575b2..0000000
--- a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Broadcast a single dispatch into multiple other dispatchables.
- *
- * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
- * see the same dispatch as well. The first target's return value is returned.</p>
- *
- * <p>This enables a single listener to be converted into a multi-listener.</p>
- */
-public class BroadcastDispatcher<T> implements Dispatchable<T> {
-
- private final List<Dispatchable<T>> mDispatchTargets;
-
- /**
- * Create a broadcast dispatcher from the supplied dispatch targets.
- *
- * @param dispatchTargets one or more targets to dispatch to
- */
- @SafeVarargs
- public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
- mDispatchTargets = Arrays.asList(
- checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
- Object result = null;
- boolean gotResult = false;
-
- for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
- Object localResult = dispatchTarget.dispatch(method, args);
-
- if (!gotResult) {
- gotResult = true;
- result = localResult;
- }
- }
-
- return result;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
deleted file mode 100644
index 753103f..0000000
--- a/core/java/android/hardware/camera2/dispatch/Dispatchable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-/**
- * Dynamically dispatch a method and its argument to some object.
- *
- * <p>This can be used to intercept method calls and do work around them, redirect work,
- * or block calls entirely.</p>
- */
-public interface Dispatchable<T> {
- /**
- * Dispatch the method and arguments to this object.
- * @param method a method defined in class {@code T}
- * @param args arguments corresponding to said {@code method}
- * @return the object returned when invoking {@code method}
- * @throws Throwable any exception that might have been raised while invoking the method
- */
- public Object dispatch(Method method, Object[] args) throws Throwable;
-}
diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
deleted file mode 100644
index 75f97e4..0000000
--- a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Duck typing dispatcher; converts dispatch methods calls from one class to another by
- * looking up equivalently methods at runtime by name.
- *
- * <p>For example, if two types have identical method names and arguments, but
- * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
- * made from one type to the other.</p>
- *
- * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
- */
-public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
-
- private final MethodNameInvoker<T> mDuck;
-
- /**
- * Create a new duck typing dispatcher.
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param targetClass destination dispatch class, methods will be converted to this class's
- */
- public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
- checkNotNull(targetClass, "targetClass must not be null");
- checkNotNull(target, "target must not be null");
-
- mDuck = new MethodNameInvoker<T>(target, targetClass);
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- return mDuck.invoke(method.getName(), args);
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
deleted file mode 100644
index f8e9d49..0000000
--- a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.os.Handler;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Forward all interface calls into a handler by posting it as a {@code Runnable}.
- *
- * <p>All calls will return immediately; functions with return values will return a default
- * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
- *
- * <p>Any exceptions thrown on the handler while trying to invoke a method
- * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
- * checked exceptions to be thrown will result in "undefined" behavior
- * (although in practice it is usually thrown as normal).</p>
- */
-public class HandlerDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "HandlerDispatcher";
-
- private final Dispatchable<T> mDispatchTarget;
- private final Handler mHandler;
-
- /**
- * Create a dispatcher that forwards it's dispatch calls by posting
- * them onto the {@code handler} as a {@code Runnable}.
- *
- * @param dispatchTarget the destination whose method calls will be redirected into the handler
- * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
- * @param <T> the type of the element you want to wrap.
- * @return a dispatcher that will forward it's dispatch calls to a handler
- */
- public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
- mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mHandler = checkNotNull(handler, "handler must not be null");
- }
-
- @Override
- public Object dispatch(final Method method, final Object[] args) throws Throwable {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- mDispatchTarget.dispatch(method, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- }
- }
- });
-
- // TODO handle primitive return values that would avoid NPE if unboxed
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
deleted file mode 100644
index ac5f526..0000000
--- a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-
-public class InvokeDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "InvocationSink";
- private final T mTarget;
-
- public InvokeDispatcher(T target) {
- mTarget = checkNotNull(target, "target must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- try {
- return method.invoke(mTarget, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- }
-
- // unreachable
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
deleted file mode 100644
index 8296b7a..0000000
--- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
- *
- * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
- */
-public class MethodNameInvoker<T> {
-
- private final Dispatchable<T> mTarget;
- private final Class<T> mTargetClass;
- private final Method[] mTargetClassMethods;
- private final ConcurrentHashMap<String, Method> mMethods =
- new ConcurrentHashMap<>();
-
- /**
- * Create a new method name invoker.
- *
- * @param target destination dispatch type, invokes will be redirected to this dispatcher
- * @param targetClass destination dispatch class, the invoked methods will be from this class
- */
- public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
- mTargetClass = targetClass;
- mTargetClassMethods = targetClass.getMethods();
- mTarget = target;
- }
-
- /**
- * Invoke a method by its name.
- *
- * <p>If more than one method exists in {@code targetClass}, the first method with the right
- * number of arguments will be used, and later calls will all use that method.</p>
- *
- * @param methodName
- * The name of the method, which will be matched 1:1 to the destination method
- * @param params
- * Variadic parameter list.
- * @return
- * The same kind of value that would normally be returned by calling {@code methodName}
- * statically.
- *
- * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
- * @throws Throwable will rethrow anything that the target method would normally throw
- */
- @SuppressWarnings("unchecked")
- public <K> K invoke(String methodName, Object... params) {
- checkNotNull(methodName, "methodName must not be null");
-
- Method targetMethod = mMethods.get(methodName);
- if (targetMethod == null) {
- for (Method method : mTargetClassMethods) {
- // TODO future: match types of params if possible
- if (method.getName().equals(methodName) &&
- (params.length == method.getParameterTypes().length) ) {
- targetMethod = method;
- mMethods.put(methodName, targetMethod);
- break;
- }
- }
-
- if (targetMethod == null) {
- throw new IllegalArgumentException(
- "Method " + methodName + " does not exist on class " + mTargetClass);
- }
- }
-
- try {
- return (K) mTarget.dispatch(targetMethod, params);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- // unreachable
- return null;
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
deleted file mode 100644
index fada075..0000000
--- a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-/**
- * Do nothing when dispatching; follows the null object pattern.
- */
-public class NullDispatcher<T> implements Dispatchable<T> {
- /**
- * Create a dispatcher that does nothing when dispatched to.
- */
- public NullDispatcher() {
- }
-
- /**
- * Do nothing; all parameters are ignored.
- */
- @Override
- public Object dispatch(Method method, Object[] args) {
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html
deleted file mode 100644
index 783d0a1..0000000
--- a/core/java/android/hardware/camera2/dispatch/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-{@hide}
-</body>
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index c9eecf1..9e4cb80 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,16 +15,17 @@
*/
package android.hardware.camera2.impl;
+import android.os.Binder;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.dispatch.Dispatchable;
-import android.hardware.camera2.dispatch.MethodNameInvoker;
import android.view.Surface;
+import java.util.concurrent.Executor;
+
import static com.android.internal.util.Preconditions.*;
/**
@@ -34,164 +35,86 @@
* to use our own proxy mechanism.</p>
*/
public class CallbackProxies {
-
- // TODO: replace with codegen
-
- public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
- private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
-
- public DeviceStateCallbackProxy(
- Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
- }
-
- @Override
- public void onOpened(CameraDevice camera) {
- mProxy.invoke("onOpened", camera);
- }
-
- @Override
- public void onDisconnected(CameraDevice camera) {
- mProxy.invoke("onDisconnected", camera);
- }
-
- @Override
- public void onError(CameraDevice camera, int error) {
- mProxy.invoke("onError", camera, error);
- }
-
- @Override
- public void onUnconfigured(CameraDevice camera) {
- mProxy.invoke("onUnconfigured", camera);
- }
-
- @Override
- public void onActive(CameraDevice camera) {
- mProxy.invoke("onActive", camera);
- }
-
- @Override
- public void onBusy(CameraDevice camera) {
- mProxy.invoke("onBusy", camera);
- }
-
- @Override
- public void onClosed(CameraDevice camera) {
- mProxy.invoke("onClosed", camera);
- }
-
- @Override
- public void onIdle(CameraDevice camera) {
- mProxy.invoke("onIdle", camera);
- }
- }
-
- @SuppressWarnings("deprecation")
- public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
- private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
-
- public DeviceCaptureCallbackProxy(
- Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
- }
-
- @Override
- public void onCaptureStarted(CameraDevice camera,
- CaptureRequest request, long timestamp, long frameNumber) {
- mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
- }
-
- @Override
- public void onCapturePartial(CameraDevice camera,
- CaptureRequest request, CaptureResult result) {
- mProxy.invoke("onCapturePartial", camera, request, result);
- }
-
- @Override
- public void onCaptureProgressed(CameraDevice camera,
- CaptureRequest request, CaptureResult partialResult) {
- mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraDevice camera,
- CaptureRequest request, TotalCaptureResult result) {
- mProxy.invoke("onCaptureCompleted", camera, request, result);
- }
-
- @Override
- public void onCaptureFailed(CameraDevice camera,
- CaptureRequest request, CaptureFailure failure) {
- mProxy.invoke("onCaptureFailed", camera, request, failure);
- }
-
- @Override
- public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, long frameNumber) {
- mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
- }
-
- @Override
- public void onCaptureSequenceAborted(CameraDevice camera,
- int sequenceId) {
- mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
- }
-
- @Override
- public void onCaptureBufferLost(CameraDevice camera,
- CaptureRequest request, Surface target, long frameNumber) {
- mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
- }
-
- }
-
public static class SessionStateCallbackProxy
extends CameraCaptureSession.StateCallback {
- private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
+ private final Executor mExecutor;
+ private final CameraCaptureSession.StateCallback mCallback;
- public SessionStateCallbackProxy(
- Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget,
- CameraCaptureSession.StateCallback.class);
+ public SessionStateCallbackProxy(Executor executor,
+ CameraCaptureSession.StateCallback callback) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
+ mCallback = checkNotNull(callback, "callback must not be null");
}
@Override
public void onConfigured(CameraCaptureSession session) {
- mProxy.invoke("onConfigured", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigured(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
- mProxy.invoke("onConfigureFailed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigureFailed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onReady(CameraCaptureSession session) {
- mProxy.invoke("onReady", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onReady(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onActive(CameraCaptureSession session) {
- mProxy.invoke("onActive", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onActive(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onCaptureQueueEmpty(CameraCaptureSession session) {
- mProxy.invoke("onCaptureQueueEmpty", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onClosed(CameraCaptureSession session) {
- mProxy.invoke("onClosed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onClosed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
- mProxy.invoke("onSurfacePrepared", session, surface);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 8b8bbc3..9cac71c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,20 +20,17 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
-import android.hardware.camera2.dispatch.BroadcastDispatcher;
-import android.hardware.camera2.dispatch.DuckTypingDispatcher;
-import android.hardware.camera2.dispatch.HandlerDispatcher;
-import android.hardware.camera2.dispatch.InvokeDispatcher;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.TaskDrainer;
import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Binder;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
import static com.android.internal.util.Preconditions.*;
@@ -51,16 +48,16 @@
private final Surface mInput;
/**
* User-specified state callback, used for outgoing events; calls to this object will be
- * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+ * automatically invoked via {@code mStateExecutor}.
*/
private final CameraCaptureSession.StateCallback mStateCallback;
- /** User-specified state handler used for outgoing state callback events */
- private final Handler mStateHandler;
+ /** User-specified state executor used for outgoing state callback events */
+ private final Executor mStateExecutor;
/** Internal camera device; used to translate calls into existing deprecated API */
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
- /** Internal handler; used for all incoming events to preserve total order */
- private final Handler mDeviceHandler;
+ /** Internal executor; used for all incoming events to preserve total order */
+ private final Executor mDeviceExecutor;
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
private final TaskDrainer<Integer> mSequenceDrainer;
@@ -87,9 +84,9 @@
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraCaptureSessionImpl(int id, Surface input,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess) {
+ Executor deviceStateExecutor, boolean configureSuccess) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
@@ -98,10 +95,11 @@
mIdString = String.format("Session %d: ", mId);
mInput = input;
- mStateHandler = checkHandler(stateHandler);
- mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
+ mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
+ mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
- mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+ mDeviceExecutor = checkNotNull(deviceStateExecutor,
+ "deviceStateExecutor must not be null");
mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
/*
@@ -110,11 +108,11 @@
* This ensures total ordering between CameraDevice.StateCallback and
* CameraDeviceImpl.CaptureCallback events.
*/
- mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+ mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
/*name*/"seq");
- mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+ mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
/*name*/"idle");
- mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+ mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
/*name*/"abort");
// CameraDevice should call configureOutputs and have it finish before constructing us
@@ -180,7 +178,7 @@
}
return addPendingSequence(mDeviceImpl.capture(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -217,7 +215,7 @@
}
return addPendingSequence(mDeviceImpl.captureBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -241,7 +239,7 @@
}
return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -274,7 +272,7 @@
}
return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@@ -446,119 +444,145 @@
}
/**
- * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
+ * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
*/
- private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
- InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
- HandlerDispatcher<StateCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
-
- return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
+ private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
+ return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
}
/**
* Forward callbacks from
* CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
*
- * <p>In particular, all calls are automatically split to go both to our own
- * internal callback, and to the user-specified callback (by transparently posting
- * to the user-specified handler).</p>
- *
* <p>When a capture sequence finishes, update the pending checked sequences set.</p>
*/
@SuppressWarnings("deprecation")
private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
Handler handler, CaptureCallback callback) {
- CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+ final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
+ handler) : null;
+ return new CameraDeviceImpl.CaptureCallback() {
@Override
public void onCaptureStarted(CameraDevice camera,
CaptureRequest request, long timestamp, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureStarted(
+ CameraCaptureSessionImpl.this, request, timestamp,
+ frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCapturePartial(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCapturePartial(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureProgressed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureProgressed(
+ CameraCaptureSessionImpl.this, request, partialResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureCompleted(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureCompleted(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureFailed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureFailed(
+ CameraCaptureSessionImpl.this, request, failure));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceCompleted(
+ CameraCaptureSessionImpl.this, sequenceId, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureSequenceAborted(CameraDevice camera,
int sequenceId) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceAborted(
+ CameraCaptureSessionImpl.this, sequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureBufferLost(CameraDevice camera,
CaptureRequest request, Surface target, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureBufferLost(
+ CameraCaptureSessionImpl.this, request, target, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
-
};
-
- /*
- * Split the calls from the device callback into local callback and the following chain:
- * - replace the first CameraDevice arg with a CameraCaptureSession
- * - duck type from device callback to session callback
- * - then forward the call to a handler
- * - then finally invoke the destination method on the session callback object
- */
- if (callback == null) {
- // OK: API allows the user to not specify a callback, and the handler may
- // also be null in that case. Collapse whole dispatch chain to only call the local
- // callback
- return localCallback;
- }
-
- InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
- new InvokeDispatcher<>(localCallback);
-
- InvokeDispatcher<CaptureCallback> userCallbackSink =
- new InvokeDispatcher<>(callback);
- HandlerDispatcher<CaptureCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
- DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
- = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
- ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
- replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
- /*argumentIndex*/0, this);
-
- BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
- new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
- replaceDeviceWithSession,
- localSink);
-
- return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
}
/**
*
- * Create an internal state callback, to be invoked on the mDeviceHandler
+ * Create an internal state callback, to be invoked on the mDeviceExecutor
*
* <p>It has a few behaviors:
* <ul>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 06c2c25..89f6172 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,6 +33,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -59,14 +60,14 @@
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraConstrainedHighSpeedCaptureSessionImpl(int id,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess,
+ Executor deviceStateExecutor, boolean configureSuccess,
CameraCharacteristics characteristics) {
mCharacteristics = characteristics;
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
- stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+ stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
}
@Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 511fa43..1f35f31 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import android.annotation.NonNull;
import android.hardware.ICameraService;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -35,6 +36,7 @@
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -47,6 +49,8 @@
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.util.Preconditions;
+
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,6 +62,8 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executor;
+
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,7 +84,7 @@
private final StateCallback mDeviceCallback;
private volatile StateCallbackKK mSessionStateCallback;
- private final Handler mDeviceHandler;
+ private final Executor mDeviceExecutor;
private final AtomicBoolean mClosing = new AtomicBoolean();
private boolean mInError = false;
@@ -241,7 +247,7 @@
}
mCameraId = cameraId;
mDeviceCallback = callback;
- mDeviceHandler = handler;
+ mDeviceExecutor = checkAndWrapHandler(handler);
mCharacteristics = characteristics;
mAppTargetSdkVersion = appTargetSdkVersion;
@@ -288,15 +294,15 @@
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
- mDeviceHandler.post(mCallOnOpened);
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnOpened);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
@@ -335,7 +341,7 @@
final boolean isError = failureIsError;
synchronized(mInterfaceLock) {
mInError = true;
- mDeviceHandler.post(new Runnable() {
+ mDeviceExecutor.execute(new Runnable() {
@Override
public void run() {
if (isError) {
@@ -423,7 +429,7 @@
}
}
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
stopRepeating();
try {
@@ -482,10 +488,10 @@
throw e;
} finally {
if (success && outputs.size() > 0) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
} else {
// Always return to the 'unconfigured' state if we didn't hit a fatal error
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
}
@@ -501,8 +507,9 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -517,7 +524,7 @@
// OutputConfiguration objects are immutable, but need to have our own array
List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
- createCaptureSessionInternal(null, currentOutputs, callback, handler,
+ createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
}
@@ -537,8 +544,9 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -566,8 +574,8 @@
currentOutputs.add(new OutputConfiguration(output));
}
createCaptureSessionInternal(inputConfig, currentOutputs,
- callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
- /*sessionParams*/ null);
+ callback, checkAndWrapHandler(handler),
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
}
@Override
@@ -582,7 +590,8 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
/*sessionParams*/ null);
}
@@ -597,8 +606,8 @@
for (OutputConfiguration output : outputs) {
currentOutputs.add(new OutputConfiguration(output));
}
- createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
- /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, currentOutputs, callback,
+ checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
}
@Override
@@ -612,14 +621,17 @@
if (outputConfigs == null) {
throw new IllegalArgumentException("Invalid output configurations");
}
+ if (config.getExecutor() == null) {
+ throw new IllegalArgumentException("Invalid executor");
+ }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
- config.getStateCallback(), config.getHandler(), config.getSessionType(),
+ config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
}
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
- CameraCaptureSession.StateCallback callback, Handler handler,
+ CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
@@ -673,12 +685,11 @@
SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
- callback, handler, this, mDeviceHandler, configureSuccess,
+ callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
- callback, handler, this, mDeviceHandler,
- configureSuccess);
+ callback, executor, this, mDeviceExecutor, configureSuccess);
}
// TODO: wait until current session closes, then create the new session
@@ -893,22 +904,22 @@
}
}
- public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
+ public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
throws CameraAccessException {
if (DEBUG) {
Log.d(TAG, "calling capture");
}
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
}
public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
}
/**
@@ -963,7 +974,12 @@
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
Log.w(TAG, String.format(
"did not register callback to request %d",
@@ -982,11 +998,11 @@
}
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
- Handler handler, boolean repeating) throws CameraAccessException {
+ Executor executor, boolean repeating) throws CameraAccessException {
- // Need a valid handler, or current thread needs to have a looper, if
+ // Need a valid executor, or current thread needs to have a looper, if
// callback is valid
- handler = checkHandler(handler, callback);
+ executor = checkExecutor(executor, callback);
// Make sure that there all requests have at least 1 surface; all surfaces are non-null;
// the surface isn't a physical stream surface for reprocessing request
@@ -1040,7 +1056,7 @@
if (callback != null) {
mCaptureCallbackMap.put(requestInfo.getRequestId(),
new CaptureCallbackHolder(
- callback, requestList, handler, repeating, mNextSessionId - 1));
+ callback, requestList, executor, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1059,7 +1075,7 @@
}
if (mIdle) {
- mDeviceHandler.post(mCallOnActive);
+ mDeviceExecutor.execute(mCallOnActive);
}
mIdle = false;
@@ -1068,18 +1084,18 @@
}
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
}
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
}
public void stopRepeating() throws CameraAccessException {
@@ -1124,12 +1140,12 @@
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
// If already idle, just do a busy->idle transition immediately, don't actually
// flush.
if (mIdle) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
return;
}
@@ -1157,7 +1173,7 @@
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
if (mRemoteDevice != null || mInError) {
- mDeviceHandler.post(mCallOnClosed);
+ mDeviceExecutor.execute(mCallOnClosed);
}
mRemoteDevice = null;
@@ -1354,7 +1370,7 @@
private final boolean mRepeating;
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final int mSessionId;
/**
* <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1366,13 +1382,13 @@
private final boolean mHasBatchedOutputs;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating, int sessionId) {
- if (callback == null || handler == null) {
+ Executor executor, boolean repeating, int sessionId) {
+ if (callback == null || executor == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
}
mRepeating = repeating;
- mHandler = handler;
+ mExecutor = executor;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
mSessionId = sessionId;
@@ -1425,8 +1441,8 @@
return getRequest(0);
}
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
public int getSessionId() {
@@ -1810,7 +1826,12 @@
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -1838,7 +1859,12 @@
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
break;
case ERROR_CAMERA_REQUEST:
case ERROR_CAMERA_RESULT:
@@ -1860,8 +1886,13 @@
private void scheduleNotifyError(int code) {
mInError = true;
- CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
- CameraDeviceCallbacks::notifyError, this, code));
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
+ CameraDeviceCallbacks::notifyError, this, code));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void notifyError(int code) {
@@ -1900,7 +1931,12 @@
if (mRemoteDevice == null) return; // Camera already closed
if (!CameraDeviceImpl.this.mIdle) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
CameraDeviceImpl.this.mIdle = true;
}
@@ -1929,36 +1965,41 @@
if (isClosed()) return;
// Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- final int subsequenceId = resultExtras.getSubsequenceId();
- final CaptureRequest request = holder.getRequest(subsequenceId);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ final int subsequenceId = resultExtras.getSubsequenceId();
+ final CaptureRequest request = holder.getRequest(subsequenceId);
- if (holder.hasBatchedOutputs()) {
- // Send derived onCaptureStarted for requests within the batch
- final Range<Integer> fpsRange =
- request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
- for (int i = 0; i < holder.getRequestCount(); i++) {
+ if (holder.hasBatchedOutputs()) {
+ // Send derived onCaptureStarted for requests within the
+ // batch
+ final Range<Integer> fpsRange =
+ request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ for (int i = 0; i < holder.getRequestCount(); i++) {
+ holder.getCallback().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(i),
+ timestamp - (subsequenceId - i) *
+ NANO_PER_SECOND/fpsRange.getUpper(),
+ frameNumber - (subsequenceId - i));
+ }
+ } else {
holder.getCallback().onCaptureStarted(
CameraDeviceImpl.this,
- holder.getRequest(i),
- timestamp - (subsequenceId - i) *
- NANO_PER_SECOND/fpsRange.getUpper(),
- frameNumber - (subsequenceId - i));
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp, frameNumber);
}
- } else {
- holder.getCallback().onCaptureStarted(
- CameraDeviceImpl.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp, frameNumber);
}
}
- }
- });
-
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2111,7 +2152,12 @@
finalResult = resultAsCapture;
}
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
// Collect the partials for a total result; or mark the frame as totally completed
mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2207,7 +2253,12 @@
}
};
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
} else {
boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2247,7 +2298,12 @@
checkAndFireSequenceComplete();
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2255,6 +2311,62 @@
} // public class CameraDeviceCallbacks
/**
+ * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+ private static class CameraHandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public CameraHandlerExecutor(@NonNull Handler handler) {
+ mHandler = Preconditions.checkNotNull(handler);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ // Return value of 'post()' will be ignored in order to keep the
+ // same camera behavior. For further details see b/74605221 .
+ mHandler.post(command);
+ }
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>
+ * If executor is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ static Executor checkExecutor(Executor executor) {
+ return (executor == null) ? checkAndWrapHandler(null) : executor;
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
+ */
+ static <T> Executor checkExecutor(Executor executor, T callback) {
+ return (callback != null) ? checkExecutor(executor) : executor;
+ }
+
+ /**
+ * Wrap Handler in Executor.
+ *
+ * <p>
+ * If handler is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ public static Executor checkAndWrapHandler(Handler handler) {
+ return new CameraHandlerExecutor(checkHandler(handler));
+ }
+
+ /**
* Default handler management.
*
* <p>
@@ -2328,6 +2440,11 @@
}
}
};
- CameraDeviceImpl.this.mDeviceHandler.post(r);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(r);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index a79a6c1..7bdb4a2 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
package android.hardware.camera2.params;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
-import android.os.Handler;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -78,7 +79,7 @@
private List<OutputConfiguration> mOutputConfigurations;
private CameraCaptureSession.StateCallback mStateCallback;
private int mSessionType;
- private Handler mHandler = null;
+ private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
private CaptureRequest mSessionParameters = null;
@@ -87,10 +88,9 @@
*
* @param sessionType The session type.
* @param outputs A list of output configurations for the capture session.
+ * @param executor The executor which should be used to invoke the callback. In general it is
+ * recommended that camera operations are not done on the main (UI) thread.
* @param cb A state callback interface implementation.
- * @param handler The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
*
* @see #SESSION_REGULAR
* @see #SESSION_HIGH_SPEED
@@ -101,11 +101,12 @@
*/
public SessionConfiguration(@SessionMode int sessionType,
@NonNull List<OutputConfiguration> outputs,
- @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
mSessionType = sessionType;
mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
mStateCallback = cb;
- mHandler = handler;
+ mExecutor = executor;
}
/**
@@ -136,14 +137,12 @@
}
/**
- * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+ * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
*
- * @return The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
+ * @return The Executor on which the callback will be invoked.
*/
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
/**
diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java
index ed30ff3..e71f26a 100644
--- a/core/java/android/hardware/camera2/utils/TaskDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
*/
package android.hardware.camera2.utils;
-import android.os.Handler;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -55,7 +55,7 @@
private static final String TAG = "TaskDrainer";
private final boolean DEBUG = false;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final DrainListener mListener;
private final String mName;
@@ -73,28 +73,27 @@
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskDrainer(Handler handler, DrainListener listener) {
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = null;
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskDrainer(Handler handler, DrainListener listener, String name) {
- // XX: Probably don't need a handler at all here
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener, String name) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = name;
}
@@ -200,15 +199,12 @@
}
private void postDrained() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ mExecutor.execute(() -> {
if (DEBUG) {
Log.v(TAG + "[" + mName + "]", "onDrained");
}
mListener.onDrained();
- }
});
}
}
diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
index f6272c9..9615450 100644
--- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,7 +16,8 @@
package android.hardware.camera2.utils;
import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-import android.os.Handler;
+
+import java.util.concurrent.Executor;
/**
* Keep track of a single concurrent task starting and finishing;
@@ -38,25 +39,25 @@
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener) {
- mTaskDrainer = new TaskDrainer<>(handler, listener);
+ public TaskSingleDrainer(Executor executor, DrainListener listener) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener);
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
- mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+ public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener, name);
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 36f359b..93b1b22 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1977,13 +1977,6 @@
* services.jar, possibly in com.android.server.net. */
/** {@hide} */
- public static final boolean checkChangePermission(Context context) {
- int uid = Binder.getCallingUid();
- return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
- .getPackageNameForUid(context, uid), false /* throwException */);
- }
-
- /** {@hide} */
public static final void enforceChangePermission(Context context) {
int uid = Binder.getCallingUid();
Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index f4b328e..57f0588 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -129,7 +129,7 @@
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
*/
- public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key) {
+ public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
this(algorithm, key, key.length * 8);
}
@@ -144,7 +144,8 @@
* @param key key padded to a multiple of 8 bits.
* @param truncLenBits number of bits of output hash to use.
*/
- public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
+ public IpSecAlgorithm(
+ @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
mName = algorithm;
mKey = key.clone();
mTruncLenBits = truncLenBits;
@@ -152,11 +153,13 @@
}
/** Get the algorithm name */
+ @NonNull
public String getName() {
return mName;
}
/** Get the key for this algorithm */
+ @NonNull
public byte[] getKey() {
return mKey.clone();
}
@@ -270,6 +273,7 @@
}
@Override
+ @NonNull
public String toString() {
return new StringBuilder()
.append("{mName=")
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index cb4299e..972b9c0 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -253,8 +253,9 @@
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
* currently allocated for this user
*/
- public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
- throws ResourceUnavailableException {
+ @NonNull
+ public SecurityParameterIndex allocateSecurityParameterIndex(
+ @NonNull InetAddress destinationAddress) throws ResourceUnavailableException {
try {
return new SecurityParameterIndex(
mService,
@@ -280,8 +281,9 @@
* @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
* reserved
*/
+ @NonNull
public SecurityParameterIndex allocateSecurityParameterIndex(
- InetAddress destinationAddress, int requestedSpi)
+ @NonNull InetAddress destinationAddress, int requestedSpi)
throws SpiUnavailableException, ResourceUnavailableException {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
@@ -318,9 +320,8 @@
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(
- Socket socket, @PolicyDirection int direction, IpSecTransform transform)
- throws IOException {
+ public void applyTransportModeTransform(@NonNull Socket socket,
+ @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
@@ -353,9 +354,8 @@
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(
- DatagramSocket socket, @PolicyDirection int direction, IpSecTransform transform)
- throws IOException {
+ public void applyTransportModeTransform(@NonNull DatagramSocket socket,
+ @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
@@ -388,9 +388,8 @@
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(
- FileDescriptor socket, @PolicyDirection int direction, IpSecTransform transform)
- throws IOException {
+ public void applyTransportModeTransform(@NonNull FileDescriptor socket,
+ @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
// We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
// constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
@@ -413,8 +412,7 @@
* @param socket a socket that previously had a transform applied to it
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(Socket socket)
- throws IOException {
+ public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
removeTransportModeTransforms(socket.getFileDescriptor$());
}
@@ -431,8 +429,7 @@
* @param socket a socket that previously had a transform applied to it
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(DatagramSocket socket)
- throws IOException {
+ public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException {
removeTransportModeTransforms(socket.getFileDescriptor$());
}
@@ -449,8 +446,7 @@
* @param socket a socket that previously had a transform applied to it
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(FileDescriptor socket)
- throws IOException {
+ public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
mService.removeTransportModeTransforms(pfd);
} catch (RemoteException e) {
@@ -588,6 +584,7 @@
// safely usable for Encapsulation without allowing a user to possibly unbind from/close
// the port, which could potentially impact the traffic of the next user who binds to that
// socket.
+ @NonNull
public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
throws IOException, ResourceUnavailableException {
/*
@@ -617,6 +614,7 @@
// safely usable for Encapsulation without allowing a user to possibly unbind from/close
// the port, which could potentially impact the traffic of the next user who binds to that
// socket.
+ @NonNull
public UdpEncapsulationSocket openUdpEncapsulationSocket()
throws IOException, ResourceUnavailableException {
return new UdpEncapsulationSocket(mService, 0);
@@ -645,6 +643,7 @@
private int mResourceId = INVALID_RESOURCE_ID;
/** Get the underlying SPI held by this object. */
+ @NonNull
public String getInterfaceName() {
return mInterfaceName;
}
@@ -659,7 +658,8 @@
* @hide
*/
@SystemApi
- public void addAddress(LinkAddress address) throws IOException {
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public void addAddress(@NonNull LinkAddress address) throws IOException {
try {
mService.addAddressToTunnelInterface(mResourceId, address);
} catch (RemoteException e) {
@@ -676,7 +676,8 @@
* @hide
*/
@SystemApi
- public void removeAddress(LinkAddress address) throws IOException {
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public void removeAddress(@NonNull LinkAddress address) throws IOException {
try {
mService.removeAddressFromTunnelInterface(mResourceId, address);
} catch (RemoteException e) {
@@ -768,7 +769,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
throws ResourceUnavailableException, IOException {
@@ -793,9 +795,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
- public void applyTunnelModeTransform(IpSecTunnelInterface tunnel,
- @PolicyDirection int direction, IpSecTransform transform) throws IOException {
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
+ @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
try {
mService.applyTunnelModeTransform(
tunnel.getResourceId(), direction, transform.getResourceId());
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 60e96f9..099fe02 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -282,7 +282,7 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
})
public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
@@ -325,7 +325,7 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
})
public void stopNattKeepalive() {
@@ -350,6 +350,7 @@
*
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
*/
+ @NonNull
public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
Preconditions.checkNotNull(algo);
@@ -364,6 +365,7 @@
*
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
*/
+ @NonNull
public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
Preconditions.checkNotNull(algo);
@@ -384,6 +386,7 @@
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
* be applied.
*/
+ @NonNull
public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
Preconditions.checkNotNull(algo);
mConfig.setAuthenticatedEncryption(algo);
@@ -403,6 +406,7 @@
* @param remotePort the UDP port number of the remote host that will send and receive
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
*/
+ @NonNull
public IpSecTransform.Builder setIpv4Encapsulation(
@NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
Preconditions.checkNotNull(localSocket);
@@ -436,6 +440,7 @@
* collides with an existing transform
* @throws IOException indicating other errors
*/
+ @NonNull
public IpSecTransform buildTransportModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
@@ -472,7 +477,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index fdcc304..4f92fa6 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -24,6 +24,7 @@
import android.util.proto.ProtoOutputStream;
import java.util.Objects;
+import java.util.Set;
/**
* Defines a request for a network, made through {@link NetworkRequest.Builder} and used
@@ -205,6 +206,19 @@
}
/**
+ * Set the watched UIDs for this request. This will be reset and wiped out unless
+ * the calling app holds the CHANGE_NETWORK_STATE permission.
+ *
+ * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+ * @return The builder to facilitate chaining.
+ * @hide
+ */
+ public Builder setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ return this;
+ }
+
+ /**
* Add a capability that must not exist in the requested network.
* <p>
* If the capability was previously added to the list of required capabilities (for
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 5312dca..f5a7433 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -357,6 +357,23 @@
}
/**
+ * Does a loose equality check between two given {@link BaseBundle} objects.
+ * Returns {@code true} if both are {@code null}, or if both are equal as per
+ * {@link #kindofEquals(BaseBundle)}
+ *
+ * @param a A {@link BaseBundle} object
+ * @param b Another {@link BaseBundle} to compare with a
+ * @return {@code true} if both are the same, {@code false} otherwise
+ *
+ * @see #kindofEquals(BaseBundle)
+ *
+ * @hide
+ */
+ public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
+ return (a == b) || (a != null && a.kindofEquals(b));
+ }
+
+ /**
* @hide This kind-of does an equality comparison. Kind-of.
*/
public boolean kindofEquals(BaseBundle other) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b16e7d7..f528d63 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -7138,13 +7138,20 @@
for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+
+ final long startTimeMs = roundUsToMs(ss.getStartTime(batteryUptimeUs, which));
+ final int starts = ss.getStarts(which);
+ final int launches = ss.getLaunches(which);
+ if (startTimeMs == 0 && starts == 0 && launches == 0) {
+ continue;
+ }
+
long sToken = proto.start(UidProto.Package.SERVICES);
proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
- proto.write(UidProto.Package.Service.START_DURATION_MS,
- roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
- proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
- proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+ proto.write(UidProto.Package.Service.START_DURATION_MS, startTimeMs);
+ proto.write(UidProto.Package.Service.START_COUNT, starts);
+ proto.write(UidProto.Package.Service.LAUNCH_COUNT, launches);
proto.end(sToken);
}
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 9ac2b3e..3bf09ec 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -20,6 +20,10 @@
import com.google.android.rappor.Encoder;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
@@ -66,7 +70,7 @@
random = sSecureRandom;
} else {
// To have deterministic result by hard coding encoder id as seed.
- random = new Random((long) config.mEncoderId.hashCode());
+ random = new Random(getInsecureSeed(config.mEncoderId));
userSecret = INSECURE_SECRET;
}
mEncoder = new Encoder(random, null, null,
@@ -75,6 +79,17 @@
config.mNumCohorts, config.mNumBloomHashes);
}
+ private long getInsecureSeed(String input) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+ return ByteBuffer.wrap(bytes).getLong();
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen
+ throw new AssertionError("Unable generate insecure seed");
+ }
+ }
+
/**
* Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 87babc0..86f02e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7762,6 +7762,21 @@
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
+ * What behavior should be invoked when the volume hush gesture is triggered
+ * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
+ *
+ * @hide
+ */
+ public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
+
+ /** @hide */ public static final int VOLUME_HUSH_OFF = 0;
+ /** @hide */ public static final int VOLUME_HUSH_VIBRATE = 1;
+ /** @hide */ public static final int VOLUME_HUSH_MUTE = 2;
+
+ private static final Validator VOLUME_HUSH_GESTURE_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
+ /**
* The number of times (integer) the user has manually enabled battery saver.
* @hide
*/
@@ -7875,6 +7890,7 @@
SCREENSAVER_ACTIVATE_ON_SLEEP,
LOCKDOWN_IN_POWER_MENU,
SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ VOLUME_HUSH_GESTURE
};
/**
@@ -8011,6 +8027,7 @@
VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+ VALIDATORS.put(VOLUME_HUSH_GESTURE, VOLUME_HUSH_GESTURE_VALIDATOR);
VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index 3d3b6d5..d42424e 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -52,7 +52,7 @@
public final class KeyChainProtectionParams implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+ @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
public @interface UserSecretType {
}
@@ -61,11 +61,6 @@
*/
public static final int TYPE_LOCKSCREEN = 100;
- /**
- * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
- */
- public static final int TYPE_CUSTOM_PASSWORD = 101;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"UI_FORMAT_"}, value = {UI_FORMAT_PIN, UI_FORMAT_PASSWORD, UI_FORMAT_PATTERN})
@@ -120,7 +115,6 @@
/**
* @see TYPE_LOCKSCREEN
- * @see TYPE_CUSTOM_PASSWORD
*/
public @UserSecretType int getUserSecretType() {
return mUserSecretType;
@@ -166,7 +160,6 @@
* Sets user secret type.
*
* @see TYPE_LOCKSCREEN
- * @see TYPE_CUSTOM_PASSWORD
* @param userSecretType The secret type
* @return This builder.
*/
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 00f54e1..69b9123 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -127,18 +127,13 @@
/**
* CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
*/
- // TODO: Change to @NonNull
- public CertPath getTrustedHardwareCertPath() {
- if (mCertPath == null) {
- return null;
- } else {
- try {
- return mCertPath.getCertPath();
- } catch (CertificateException e) {
- // Rethrow an unchecked exception as it should not happen. If such an issue exists,
- // an exception should have been thrown during service initialization.
- throw new BadParcelableException(e);
- }
+ public @NonNull CertPath getTrustedHardwareCertPath() {
+ try {
+ return mCertPath.getCertPath();
+ } catch (CertificateException e) {
+ // Rethrow an unchecked exception as it should not happen. If such an issue exists,
+ // an exception should have been thrown during service initialization.
+ throw new BadParcelableException(e);
}
}
@@ -248,13 +243,9 @@
* @throws CertificateException if the given certificate path cannot be encoded properly
* @return This builder.
*/
- public Builder setTrustedHardwareCertPath(CertPath certPath) throws CertificateException {
- // TODO: Make it NonNull when the caller code is all updated
- if (certPath == null) {
- mInstance.mCertPath = null;
- } else {
- mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
- }
+ public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
+ throws CertificateException {
+ mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
return this;
}
@@ -282,7 +273,7 @@
}
/**
- * Sets recovery key blob
+ * Sets recovery key blob.
*
* @param encryptedRecoveryKeyBlob The recovery key blob.
* @return This builder.
@@ -297,7 +288,7 @@
* Creates a new {@link KeyChainSnapshot} instance.
*
* @return new instance
- * @throws NullPointerException if some required fields were not set.
+ * @throws NullPointerException if some of the required fields were not set.
*/
@NonNull public KeyChainSnapshot build() {
Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
@@ -306,6 +297,7 @@
"entryRecoveryData");
Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
Preconditions.checkNotNull(mInstance.mServerParams);
+ Preconditions.checkNotNull(mInstance.mCertPath);
return mInstance;
}
}
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index 428eaaa..8cb8e51 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -38,7 +38,7 @@
public final class KeyDerivationParams implements Parcelable {
private final int mAlgorithm;
private final byte[] mSalt;
- private final int mDifficulty;
+ private final int mMemoryDifficulty;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -53,25 +53,32 @@
/**
* SCRYPT.
- *
- * @hide
*/
public static final int ALGORITHM_SCRYPT = 2;
/**
- * Creates instance of the class to to derive key using salted SHA256 hash.
+ * Creates instance of the class to to derive keys using salted SHA256 hash.
+ *
+ * <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
+ * salt + key_material_len + key_material, where salt_len and key_material_len are one-byte, and
+ * denote the number of bytes for salt and key_material, respectively.
*/
public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
return new KeyDerivationParams(ALGORITHM_SHA256, salt);
}
/**
- * Creates instance of the class to to derive key using the password hashing algorithm SCRYPT.
+ * Creates instance of the class to to derive keys using the password hashing algorithm SCRYPT.
*
- * @hide
+ * <p>We expose only one tuning parameter of SCRYPT, which is the memory cost parameter (i.e. N
+ * in <a href="https://www.tarsnap.com/scrypt/scrypt.pdf">the SCRYPT paper</a>). Regular/default
+ * values are used for the other parameters, to keep the overall running time low. Specifically,
+ * the parallelization parameter p is 1, the block size parameter r is 8, and the hashing output
+ * length is 32-byte.
*/
- public static KeyDerivationParams createScryptParams(@NonNull byte[] salt, int difficulty) {
- return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, difficulty);
+ public static KeyDerivationParams createScryptParams(
+ @NonNull byte[] salt, int memoryDifficulty) {
+ return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, memoryDifficulty);
}
/**
@@ -79,17 +86,17 @@
*/
// TODO: Make private once legacy API is removed
public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
- this(algorithm, salt, /*difficulty=*/ 0);
+ this(algorithm, salt, /*memoryDifficulty=*/ -1);
}
/**
* @hide
*/
KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
- int difficulty) {
+ int memoryDifficulty) {
mAlgorithm = algorithm;
mSalt = Preconditions.checkNotNull(salt);
- mDifficulty = difficulty;
+ mMemoryDifficulty = memoryDifficulty;
}
/**
@@ -107,12 +114,16 @@
}
/**
- * Gets hashing difficulty.
+ * Gets the memory difficulty parameter for the hashing algorithm.
*
- * @hide
+ * <p>The effect of this parameter depends on the algorithm in use. For example, please see
+ * {@link #createScryptParams(byte[], int)} for choosing the parameter for SCRYPT.
+ *
+ * <p>If the specific algorithm does not support such a memory difficulty parameter, its value
+ * should be -1.
*/
- public int getDifficulty() {
- return mDifficulty;
+ public int getMemoryDifficulty() {
+ return mMemoryDifficulty;
}
public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
@@ -130,7 +141,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mAlgorithm);
out.writeByteArray(mSalt);
- out.writeInt(mDifficulty);
+ out.writeInt(mMemoryDifficulty);
}
/**
@@ -139,7 +150,7 @@
protected KeyDerivationParams(Parcel in) {
mAlgorithm = in.readInt();
mSalt = in.createByteArray();
- mDifficulty = in.readInt();
+ mMemoryDifficulty = in.readInt();
}
@Override
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 503387a..aa9d1c0 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -266,8 +266,8 @@
/**
* Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
- * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
- * most one registered listener at any time.
+ * #getKeyChainSnapshot} can be used to get the snapshot. Note that every recovery agent can
+ * have at most one registered listener at any time.
*
* @param intent triggered when new snapshot is available. Unregisters listener if the value is
* {@code null}.
@@ -292,12 +292,13 @@
* in vaultParams {@link RecoverySession#start(CertPath, byte[], byte[], List)}.
*
* @param serverParams included in recovery key blob.
- * @see #getRecoveryData
+ * @see #getKeyChainSnapshot
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+ public void setServerParams(@NonNull byte[] serverParams)
+ throws InternalRecoveryServiceException {
try {
mBinder.setServerParams(serverParams);
} catch (RemoteException e) {
@@ -321,7 +322,7 @@
* Returns a list of aliases of keys belonging to the application.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public List<String> getAliases() throws InternalRecoveryServiceException {
+ public @NonNull List<String> getAliases() throws InternalRecoveryServiceException {
try {
Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
return new ArrayList<>(allStatuses.keySet());
@@ -355,7 +356,7 @@
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void setRecoveryStatus(String alias, int status)
+ public void setRecoveryStatus(@NonNull String alias, int status)
throws InternalRecoveryServiceException {
try {
mBinder.setRecoveryStatus(alias, status);
@@ -390,7 +391,7 @@
* service.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+ public int getRecoveryStatus(@NonNull String alias) throws InternalRecoveryServiceException {
try {
Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
Integer status = allStatuses.get(alias);
@@ -410,8 +411,7 @@
* Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
* is necessary to recover data.
*
- * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} or {@link
- * KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}
+ * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN}
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
@@ -450,51 +450,6 @@
}
/**
- * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
- * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
- * called.
- *
- * @return list of recovery secret types
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
- */
- @NonNull
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
- throws InternalRecoveryServiceException {
- try {
- return mBinder.getPendingRecoverySecretTypes();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw wrapUnexpectedServiceSpecificException(e);
- }
- }
-
- /**
- * Method notifies KeyStore that a user-generated secret is available. This method generates a
- * symmetric session key which a trusted remote device can use to return a recovery key. Caller
- * should use {@link KeyChainProtectionParams#clearSecret} to override the secret value in
- * memory.
- *
- * @param recoverySecret user generated secret together with parameters necessary to regenerate
- * it on a new device.
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
- */
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
- throws InternalRecoveryServiceException {
- try {
- mBinder.recoverySecretAvailable(recoverySecret);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw wrapUnexpectedServiceSpecificException(e);
- }
- }
-
- /**
* Deprecated.
* Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
* key store. Returns the raw material of the key.
@@ -651,7 +606,7 @@
* <p>A recovery session is required to restore keys from a remote store.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public RecoverySession createRecoverySession() {
+ public @NonNull RecoverySession createRecoverySession() {
return RecoverySession.newInstance(this);
}
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 208b9b2..cf8a9dd 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -49,7 +49,8 @@
private final String mSessionId;
private final RecoveryController mRecoveryController;
- private RecoverySession(RecoveryController recoveryController, String sessionId) {
+ private RecoverySession(@NonNull RecoveryController recoveryController,
+ @NonNull String sessionId) {
mRecoveryController = recoveryController;
mSessionId = sessionId;
}
@@ -58,14 +59,14 @@
* A new session, started by the {@link RecoveryController}.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- static RecoverySession newInstance(RecoveryController recoveryController) {
+ static @NonNull RecoverySession newInstance(RecoveryController recoveryController) {
return new RecoverySession(recoveryController, newSessionId());
}
/**
* Returns a new random session ID.
*/
- private static String newSessionId() {
+ private static @NonNull String newSessionId() {
SecureRandom secureRandom = new SecureRandom();
byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
secureRandom.nextBytes(sessionId);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 0f7cea2..60537a4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -527,6 +527,9 @@
* <pre> <autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
* <compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/>
* </autofill-service></pre>
+ *
+ * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
+ * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index de23455..b7ec281 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -198,14 +198,6 @@
} else {
maxVersionCode = Long.MAX_VALUE;
}
- if (true) { // TODO(b/74445943): remove block after P DP2 is branched
- final String urlBarResourceId = cpAttributes.getString(
- R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId);
- if (urlBarResourceId != null) {
- Log.e(TAG, "Service is using deprecated attribute 'urlBarResourceId'");
- }
- }
-
if (compatibilityPackages == null) {
compatibilityPackages = new ArrayMap<>();
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index f32dee1..ccec483 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -149,24 +149,33 @@
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder builder = new StringBuilder("Dataset[id=");
+ final StringBuilder builder = new StringBuilder("Dataset[");
if (mId == null) {
- builder.append("null");
+ builder.append("noId");
} else {
// Cannot disclose id because it could contain PII.
- builder.append(mId.length()).append("_chars");
+ builder.append("id=").append(mId.length()).append("_chars");
}
+ if (mFieldIds != null) {
+ builder.append(", fieldIds=").append(mFieldIds);
+ }
+ if (mFieldValues != null) {
+ builder.append(", fieldValues=").append(mFieldValues);
+ }
+ if (mFieldPresentations != null) {
+ builder.append(", fieldPresentations=").append(mFieldPresentations.size());
- return builder
- .append(", fieldIds=").append(mFieldIds)
- .append(", fieldValues=").append(mFieldValues)
- .append(", fieldPresentations=")
- .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
- .append(", fieldFilters=")
- .append(mFieldFilters == null ? 0 : mFieldFilters.size())
- .append(", hasPresentation=").append(mPresentation != null)
- .append(", hasAuthentication=").append(mAuthentication != null)
- .append(']').toString();
+ }
+ if (mFieldFilters != null) {
+ builder.append(", fieldFilters=").append(mFieldFilters.size());
+ }
+ if (mPresentation != null) {
+ builder.append(", hasPresentation");
+ }
+ if (mAuthentication != null) {
+ builder.append(", hasAuthentication");
+ }
+ return builder.append(']').toString();
}
/**
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index be84ba9..2bc4b8f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -557,23 +557,40 @@
if (!sDebug) return super.toString();
// TODO: create a dump() method instead
- return new StringBuilder(
- "FillResponse : [mRequestId=" + mRequestId)
- .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
- .append(", saveInfo=").append(mSaveInfo)
- .append(", clientState=").append(mClientState != null)
- .append(", hasPresentation=").append(mPresentation != null)
- .append(", hasHeader=").append(mHeader != null)
- .append(", hasFooter=").append(mFooter != null)
- .append(", hasAuthentication=").append(mAuthentication != null)
- .append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
- .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
- .append(", disableDuration=").append(mDisableDuration)
- .append(", flags=").append(mFlags)
- .append(", fieldClassificationIds=")
- .append(Arrays.toString(mFieldClassificationIds))
- .append("]")
- .toString();
+ final StringBuilder builder = new StringBuilder(
+ "FillResponse : [mRequestId=" + mRequestId);
+ if (mDatasets != null) {
+ builder.append(", datasets=").append(mDatasets.getList());
+ }
+ if (mSaveInfo != null) {
+ builder.append(", saveInfo=").append(mSaveInfo);
+ }
+ if (mClientState != null) {
+ builder.append(", hasClientState");
+ }
+ if (mPresentation != null) {
+ builder.append(", hasPresentation");
+ }
+ if (mHeader != null) {
+ builder.append(", hasHeader");
+ }
+ if (mFooter != null) {
+ builder.append(", hasFooter");
+ }
+ if (mAuthentication != null) {
+ builder.append(", hasAuthentication");
+ }
+ if (mAuthenticationIds != null) {
+ builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
+ }
+ builder.append(", disableDuration=").append(mDisableDuration);
+ if (mFlags != 0) {
+ builder.append(", flags=").append(mFlags);
+ }
+ if (mFieldClassificationIds != null) {
+ builder.append(Arrays.toString(mFieldClassificationIds));
+ }
+ return builder.append("]").toString();
}
/////////////////////////////////////
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 1be1be6..4943fc8 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -689,22 +689,37 @@
public String toString() {
if (!sDebug) return super.toString();
- return new StringBuilder("SaveInfo: [type=")
+ final StringBuilder builder = new StringBuilder("SaveInfo: [type=")
.append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
.append(", requiredIds=").append(Arrays.toString(mRequiredIds))
- .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
- .append(", description=").append(mDescription)
- .append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
- mNegativeButtonStyle))
- .append(", flags=").append(mFlags)
- .append(", customDescription=").append(mCustomDescription)
- .append(", validator=").append(mValidator)
- .append(", sanitizerKeys=")
- .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
- .append(", sanitizerValues=")
- .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
- .append(", triggerId=").append(mTriggerId)
- .append("]").toString();
+ .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class,
+ "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle));
+ if (mOptionalIds != null) {
+ builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds));
+ }
+ if (mDescription != null) {
+ builder.append(", description=").append(mDescription);
+ }
+ if (mFlags != 0) {
+ builder.append(", flags=").append(mFlags);
+ }
+ if (mCustomDescription != null) {
+ builder.append(", customDescription=").append(mCustomDescription);
+ }
+ if (mValidator != null) {
+ builder.append(", validator=").append(mValidator);
+ }
+ if (mSanitizerKeys != null) {
+ builder.append(", sanitizerKeys=").append(mSanitizerKeys.length);
+ }
+ if (mSanitizerValues != null) {
+ builder.append(", sanitizerValues=").append(mSanitizerValues.length);
+ }
+ if (mTriggerId != null) {
+ builder.append(", triggerId=").append(mTriggerId);
+ }
+
+ return builder.append("]").toString();
}
/////////////////////////////////////
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 18431ca..febca7ec9 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -704,7 +704,12 @@
// Spans other than ReplacementSpan can be ignored because line top and bottom are
// disjunction of all tops and bottoms, although it's not optimal.
final Paint paint = getPaint();
- paint.getTextBounds(text, start, end, mTempRect);
+ if (text instanceof PrecomputedText) {
+ PrecomputedText precomputed = (PrecomputedText) text;
+ precomputed.getBounds(start, end, mTempRect);
+ } else {
+ paint.getTextBounds(text, start, end, mTempRect);
+ }
final Paint.FontMetricsInt fm = paint.getFontMetricsInt();
return mTempRect.top < fm.top || mTempRect.bottom > fm.bottom;
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 980f4704..a107cab 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -297,6 +298,18 @@
}
/**
+ * Retrieves the bounding rectangle that encloses all of the characters, with an implied origin
+ * at (0, 0).
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull Rect bounds) {
+ nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
+ paint.getBidiFlags(), bounds);
+ }
+
+ /**
* Generates new MeasuredParagraph for Bidi computation.
*
* If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -728,4 +741,7 @@
@CriticalNative
private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
+ int end, int bidiFlag, Rect rect);
}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 9458184..413df05 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,6 +19,8 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.text.style.MetricAffectingSpan;
import com.android.internal.util.Preconditions;
@@ -57,10 +59,12 @@
* </pre>
*
* Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
+ * {@link android.widget.TextView} will be rejected.
+ *
+ * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
+ * PrecomputedText.
*/
-public class PrecomputedText implements Spanned {
+public class PrecomputedText implements Spannable {
private static final char LINE_FEED = '\n';
/**
@@ -283,7 +287,7 @@
// The original text.
- private final @NonNull SpannedString mText;
+ private final @NonNull SpannableString mText;
// The inclusive start offset of the measuring target.
private final @IntRange(from = 0) int mStart;
@@ -304,6 +308,9 @@
* presented can save work on the UI thread.
* </p>
*
+ * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the
+ * created PrecomputedText.
+ *
* @param text the text to be measured
* @param params parameters that define how text will be precomputed
* @return A {@link PrecomputedText}
@@ -347,7 +354,7 @@
private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
@IntRange(from = 0) int end, @NonNull Params params,
@NonNull ParagraphInfo[] paraInfo) {
- mText = new SpannedString(text);
+ mText = new SpannableString(text, true /* ignoreNoCopySpan */);
mStart = start;
mEnd = end;
mParams = params;
@@ -457,6 +464,21 @@
return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
}
+ /** @hide */
+ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @NonNull Rect bounds) {
+ final int paraIndex = findParaIndex(start);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ if (start < paraStart || paraEnd < end) {
+ throw new RuntimeException("Cannot measured across the paragraph:"
+ + "para: (" + paraStart + ", " + paraEnd + "), "
+ + "request: (" + start + ", " + end + ")");
+ }
+ getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
+ start - paraStart, end - paraStart, bounds);
+ }
+
/**
* Returns the size of native PrecomputedText memory usage.
*
@@ -472,6 +494,35 @@
}
///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spannable overrides
+ //
+ // Do not allow to modify MetricAffectingSpan
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void setSpan(Object what, int start, int end, int flags) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be set to PrecomputedText.");
+ }
+ mText.setSpan(what, start, end, flags);
+ }
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void removeSpan(Object what) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be removed from PrecomputedText.");
+ }
+ mText.removeSpan(what);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
// Spanned overrides
//
// Just proxy for underlying mText if appropriate.
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 3a22db2..435e324 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -650,7 +650,8 @@
final CharSequence truncatedText = text.subSequence(
0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
- final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options);
+ final Supplier<TextLinks> supplier = () ->
+ classifier.generateLinks(truncatedText, options.setLegacyFallback(true));
final Consumer<TextLinks> consumer = links -> {
if (links.getLinks().isEmpty()) {
if (callback != null) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 7ff4f21..d4610a5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -353,8 +353,8 @@
private int mFormat = PixelFormat.OPAQUE;
private String mName;
private SurfaceControl mParent;
- private int mWindowType;
- private int mOwnerUid;
+ private int mWindowType = -1;
+ private int mOwnerUid = -1;
/**
* Begin building a SurfaceControl with a given {@link SurfaceSession}.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 69938cb..abc19d0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2882,9 +2882,8 @@
/**
* @hide
*/
- public String toString(String prefix) {
- StringBuilder sb = new StringBuilder(256);
- sb.append("{(");
+ public void dumpDimensions(StringBuilder sb) {
+ sb.append('(');
sb.append(x);
sb.append(',');
sb.append(y);
@@ -2895,6 +2894,15 @@
sb.append((height == MATCH_PARENT ? "fill" : (height == WRAP_CONTENT
? "wrap" : String.valueOf(height))));
sb.append(")");
+ }
+
+ /**
+ * @hide
+ */
+ public String toString(String prefix) {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append('{');
+ dumpDimensions(sb);
if (horizontalMargin != 0) {
sb.append(" hm=");
sb.append(horizontalMargin);
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index c783cae..2a24dd7 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -30,6 +30,8 @@
import android.service.textclassifier.ITextSelectionCallback;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.Preconditions;
import java.util.concurrent.CountDownLatch;
@@ -37,8 +39,10 @@
/**
* Proxy to the system's default TextClassifier.
+ * @hide
*/
-final class SystemTextClassifier implements TextClassifier {
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class SystemTextClassifier implements TextClassifier {
private static final String LOG_TAG = "SystemTextClassifier";
@@ -53,13 +57,13 @@
@GuardedBy("mLoggerLock")
private Logger mLogger;
- SystemTextClassifier(Context context, TextClassificationConstants settings)
+ public SystemTextClassifier(Context context, TextClassificationConstants settings)
throws ServiceManager.ServiceNotFoundException {
mManagerService = ITextClassifierService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
mSettings = Preconditions.checkNotNull(settings);
mFallback = new TextClassifierImpl(context, settings);
- mPackageName = context.getPackageName();
+ mPackageName = Preconditions.checkNotNull(context.getPackageName());
}
/**
@@ -124,8 +128,9 @@
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Utils.validate(text, false /* allowInMainThread */);
- if (!mSettings.isSmartLinkifyEnabled()) {
- return TextClassifier.NO_OP.generateLinks(text, options);
+ final boolean legacyFallback = options != null && options.isLegacyFallback();
+ if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
+ return Utils.generateLegacyLinks(text, options);
}
try {
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index c91116a..b5c9de9 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -344,6 +344,25 @@
}
}
+ /**
+ * Triggers the specified intent.
+ *
+ * @throws IllegalArgumentException if context or intent is null
+ * @hide
+ */
+ public static void fireIntent(@NonNull final Context context, @NonNull final Intent intent) {
+ switch (getIntentType(intent, context)) {
+ case IntentType.ACTIVITY:
+ context.startActivity(intent);
+ return;
+ case IntentType.SERVICE:
+ context.startService(intent);
+ return;
+ default:
+ return;
+ }
+ }
+
@IntentType
private static int getIntentType(@NonNull Intent intent, @NonNull Context context) {
Preconditions.checkArgument(context != null);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 887bebb..98fa574 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -26,6 +26,12 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.URLSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -37,6 +43,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Interface for providing text classification related features.
@@ -511,6 +518,65 @@
Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
}
+ /**
+ * Generates links using legacy {@link Linkify}.
+ */
+ public static TextLinks generateLegacyLinks(
+ @NonNull CharSequence text, @NonNull TextLinks.Options options) {
+ final String string = Preconditions.checkNotNull(text).toString();
+ final TextLinks.Builder links = new TextLinks.Builder(string);
+
+ final List<String> entities = Preconditions.checkNotNull(options).getEntityConfig()
+ .resolveEntityListModifications(Collections.emptyList());
+ if (entities.contains(TextClassifier.TYPE_URL)) {
+ addLinks(links, string, TextClassifier.TYPE_URL);
+ }
+ if (entities.contains(TextClassifier.TYPE_PHONE)) {
+ addLinks(links, string, TextClassifier.TYPE_PHONE);
+ }
+ if (entities.contains(TextClassifier.TYPE_EMAIL)) {
+ addLinks(links, string, TextClassifier.TYPE_EMAIL);
+ }
+ // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+ return links.build();
+ }
+
+ private static void addLinks(
+ TextLinks.Builder links, String string, @EntityType String entityType) {
+ final Spannable spannable = new SpannableString(string);
+ if (Linkify.addLinks(spannable, linkMask(entityType))) {
+ final URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
+ for (URLSpan urlSpan : spans) {
+ links.addLink(
+ spannable.getSpanStart(urlSpan),
+ spannable.getSpanEnd(urlSpan),
+ entityScores(entityType),
+ urlSpan);
+ }
+ }
+ }
+
+ @LinkifyMask
+ private static int linkMask(@EntityType String entityType) {
+ switch (entityType) {
+ case TextClassifier.TYPE_URL:
+ return Linkify.WEB_URLS;
+ case TextClassifier.TYPE_PHONE:
+ return Linkify.PHONE_NUMBERS;
+ case TextClassifier.TYPE_EMAIL:
+ return Linkify.EMAIL_ADDRESSES;
+ default:
+ // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+ return 0;
+ }
+ }
+
+ private static Map<String, Float> entityScores(@EntityType String entityType) {
+ final Map<String, Float> scores = new ArrayMap<>();
+ scores.put(entityType, 1f);
+ return scores;
+ }
+
private static void checkMainThread(boolean allowInMainThread) {
if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 89e6262..c2fb032 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -209,13 +209,15 @@
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
+
+ final boolean legacyFallback = options != null && options.isLegacyFallback();
+ if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
+ return Utils.generateLegacyLinks(text, options);
+ }
+
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
- if (!mSettings.isSmartLinkifyEnabled()) {
- return builder.build();
- }
-
try {
final long startTimeMs = System.currentTimeMillis();
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index af9fc7d..38a7d9a 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,17 +20,21 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Spannable;
import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.text.util.Linkify.LinkifyMask;
import android.view.View;
import android.view.textclassifier.TextClassifier.EntityType;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -197,6 +201,7 @@
private final EntityConfidence mEntityScores;
private final int mStart;
private final int mEnd;
+ @Nullable final URLSpan mUrlSpan;
/**
* Create a new TextLink.
@@ -204,16 +209,19 @@
* @param start The start index of the identified subsequence
* @param end The end index of the identified subsequence
* @param entityScores A mapping of entity type to confidence score
+ * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
*
* @throws IllegalArgumentException if entityScores is null or empty
*/
- TextLink(int start, int end, Map<String, Float> entityScores) {
+ TextLink(int start, int end, Map<String, Float> entityScores,
+ @Nullable URLSpan urlSpan) {
Preconditions.checkNotNull(entityScores);
Preconditions.checkArgument(!entityScores.isEmpty());
Preconditions.checkArgument(start <= end);
mStart = start;
mEnd = end;
mEntityScores = new EntityConfidence(entityScores);
+ mUrlSpan = urlSpan;
}
/**
@@ -291,6 +299,7 @@
mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
mStart = in.readInt();
mEnd = in.readInt();
+ mUrlSpan = null;
}
}
@@ -301,6 +310,7 @@
private LocaleList mDefaultLocales;
private TextClassifier.EntityConfig mEntityConfig;
+ private boolean mLegacyFallback;
private @ApplyStrategy int mApplyStrategy;
private Function<TextLink, TextLinkSpan> mSpanFactory;
@@ -354,6 +364,17 @@
}
/**
+ * Sets whether the TextClassifier can fallback to legacy links if smart linkify is
+ * disabled.
+ * <strong>Note: </strong>This is not parcelled.
+ * @hide
+ */
+ public Options setLegacyFallback(boolean legacyFallback) {
+ mLegacyFallback = legacyFallback;
+ return this;
+ }
+
+ /**
* Sets a strategy for resolving conflicts when applying generated links to text that
* already have links.
*
@@ -406,6 +427,16 @@
}
/**
+ * Returns whether the TextClassifier can fallback to legacy links if smart linkify is
+ * disabled.
+ * <strong>Note: </strong>This is not parcelled.
+ * @hide
+ */
+ public boolean isLegacyFallback() {
+ return mLegacyFallback;
+ }
+
+ /**
* @return the strategy for resolving conflictswhen applying generated links to text that
* already have links
*
@@ -497,7 +528,7 @@
private final TextLink mTextLink;
- public TextLinkSpan(@Nullable TextLink textLink) {
+ public TextLinkSpan(@NonNull TextLink textLink) {
mTextLink = textLink;
}
@@ -505,13 +536,38 @@
public void onClick(View widget) {
if (widget instanceof TextView) {
final TextView textView = (TextView) widget;
- textView.requestActionMode(this);
+ final Context context = textView.getContext();
+ if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
+ if (textView.requestFocus()) {
+ textView.requestActionMode(this);
+ } else {
+ // If textView can not take focus, then simply handle the click as it will
+ // be difficult to get rid of the floating action mode.
+ textView.handleClick(this);
+ }
+ } else {
+ if (mTextLink.mUrlSpan != null) {
+ mTextLink.mUrlSpan.onClick(textView);
+ } else {
+ textView.handleClick(this);
+ }
+ }
}
}
public final TextLink getTextLink() {
return mTextLink;
}
+
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @Nullable
+ public final String getUrl() {
+ if (mTextLink.mUrlSpan != null) {
+ return mTextLink.mUrlSpan.getURL();
+ }
+ return null;
+ }
}
/**
@@ -534,12 +590,24 @@
/**
* Adds a TextLink.
*
- * @return this instance
+ * @param start The start index of the identified subsequence
+ * @param end The end index of the identified subsequence
+ * @param entityScores A mapping of entity type to confidence score
*
* @throws IllegalArgumentException if entityScores is null or empty.
*/
public Builder addLink(int start, int end, Map<String, Float> entityScores) {
- mLinks.add(new TextLink(start, end, entityScores));
+ mLinks.add(new TextLink(start, end, entityScores, null));
+ return this;
+ }
+
+ /**
+ * @see #addLink(int, int, Map)
+ * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
+ */
+ Builder addLink(int start, int end, Map<String, Float> entityScores,
+ @Nullable URLSpan urlSpan) {
+ mLinks.add(new TextLink(start, end, entityScores, urlSpan));
return this;
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index e2601dc..df04beb 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -215,6 +215,10 @@
mWindow.destroy();
mWindow = null;
}
+ mPrevPosInView.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevPosInView.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevStartCoordsInSurface.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+ mPrevStartCoordsInSurface.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
}
}
@@ -321,19 +325,24 @@
// Clamp copy coordinates inside the surface to avoid displaying distorted content.
final int clampedStartXInSurface = Math.max(0,
- Math.min(startXInSurface, surfaceWidth - mWindowWidth));
+ Math.min(startXInSurface, surfaceWidth - mBitmapWidth));
final int clampedStartYInSurface = Math.max(0,
- Math.min(startYInSurface, surfaceHeight - mWindowHeight));
+ Math.min(startYInSurface, surfaceHeight - mBitmapHeight));
+
+ // Clamp window coordinates inside the parent surface, to avoid displaying
+ // the magnifier out of screen or overlapping with system insets.
+ final Rect insets = mView.getRootWindowInsets().getSystemWindowInsets();
+ final int windowCoordsX = Math.max(insets.left,
+ Math.min(surfaceWidth - mWindowWidth - insets.right, mWindowCoords.x));
+ final int windowCoordsY = Math.max(insets.top,
+ Math.min(surfaceHeight - mWindowHeight - insets.bottom, mWindowCoords.y));
// Perform the pixel copy.
mPixelCopyRequestRect.set(clampedStartXInSurface,
clampedStartYInSurface,
clampedStartXInSurface + mBitmapWidth,
clampedStartYInSurface + mBitmapHeight);
- final int windowCoordsX = mWindowCoords.x;
- final int windowCoordsY = mWindowCoords.y;
final InternalPopupWindow currentWindowInstance = mWindow;
-
final Bitmap bitmap =
Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index dab0f73..f52854a 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -33,6 +33,7 @@
// TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden.
/**
+ * @hide
* A View that contains the controls for MediaPlayer2.
* It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
* "Subtitle", "Full Screen", and it is also possible to add multiple custom buttons.
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index be8c34c..6e855ba 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -246,7 +246,7 @@
mTextView.invalidate();
}
mTextClassification = result.mClassification;
- } else if (actionMode == Editor.TextActionMode.TEXT_LINK) {
+ } else if (result != null && actionMode == Editor.TextActionMode.TEXT_LINK) {
mTextClassification = result.mClassification;
} else {
mTextClassification = null;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 72c8426..c366a91 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -162,6 +162,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
@@ -187,6 +188,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* A user interface element that displays text to the user.
@@ -5635,6 +5640,8 @@
needEditableForNotification = true;
}
+ PrecomputedText precomputed =
+ (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
if (type == BufferType.EDITABLE || getKeyListener() != null
|| needEditableForNotification) {
createEditorIfNeeded();
@@ -5644,10 +5651,7 @@
setFilters(t, mFilters);
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
- } else if (type == BufferType.SPANNABLE || mMovement != null) {
- text = mSpannableFactory.newSpannable(text);
- } else if (text instanceof PrecomputedText) {
- PrecomputedText precomputed = (PrecomputedText) text;
+ } else if (precomputed != null) {
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
@@ -5660,6 +5664,8 @@
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
}
+ } else if (type == BufferType.SPANNABLE || mMovement != null) {
+ text = mSpannableFactory.newSpannable(text);
} else if (!(text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -11515,16 +11521,13 @@
}
/**
- * Starts an ActionMode for the specified TextLink.
+ * Starts an ActionMode for the specified TextLinkSpan.
*
* @return Whether or not we're attempting to start the action mode.
* @hide
*/
public boolean requestActionMode(@NonNull TextLinks.TextLinkSpan clickedSpan) {
Preconditions.checkNotNull(clickedSpan);
- final TextLinks.TextLink link = clickedSpan.getTextLink();
- Preconditions.checkNotNull(link);
- createEditorIfNeeded();
if (!(mText instanceof Spanned)) {
return false;
@@ -11533,15 +11536,55 @@
final int start = ((Spanned) mText).getSpanStart(clickedSpan);
final int end = ((Spanned) mText).getSpanEnd(clickedSpan);
- if (start < 0 || end < 1) {
+ if (start < 0 || end > mText.length() || start >= end) {
return false;
}
+ createEditorIfNeeded();
mEditor.startLinkActionModeAsync(start, end);
return true;
}
/**
+ * Handles a click on the specified TextLinkSpan.
+ *
+ * @return Whether or not the click is being handled.
+ * @hide
+ */
+ public boolean handleClick(@NonNull TextLinks.TextLinkSpan clickedSpan) {
+ Preconditions.checkNotNull(clickedSpan);
+ if (mText instanceof Spanned) {
+ final Spanned spanned = (Spanned) mText;
+ final int start = spanned.getSpanStart(clickedSpan);
+ final int end = spanned.getSpanEnd(clickedSpan);
+ if (start >= 0 && end <= mText.length() && start < end) {
+ final TextClassification.Options options = new TextClassification.Options()
+ .setDefaultLocales(getTextLocales());
+ final Supplier<TextClassification> supplier = () ->
+ getTextClassifier().classifyText(mText, start, end, options);
+ final Consumer<TextClassification> consumer = classification -> {
+ if (classification != null) {
+ final Intent intent = classification.getIntent();
+ if (intent != null) {
+ TextClassification.fireIntent(mContext, intent);
+ } else {
+ Log.d(LOG_TAG, "No link action to perform");
+ }
+ } else {
+ // classification == null
+ Log.d(LOG_TAG, "Timeout while classifying text");
+ }
+ };
+ CompletableFuture.supplyAsync(supplier)
+ .completeOnTimeout(null, 1, TimeUnit.SECONDS)
+ .thenAccept(consumer);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* @hide
*/
protected void stopTextActionMode() {
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 214ff3a..388eae2 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -47,6 +47,7 @@
// TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted.
/**
+ * @hide
* Displays a video file. VideoView2 class is a View class which is wrapping {@link MediaPlayer2}
* so that developers can easily implement a video rendering application.
*
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index bbff515..3c150c1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -138,7 +138,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 176 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 177 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -1551,27 +1551,31 @@
}
}
+ @VisibleForTesting
public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
- long mCount;
- long mLoadedCount;
- long mUnpluggedCount;
+ public long mCount;
+ public long mCurrentCount;
+ public long mLoadedCount;
+ public long mUnpluggedCount;
- LongSamplingCounter(TimeBase timeBase, Parcel in) {
+ public LongSamplingCounter(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
mCount = in.readLong();
+ mCurrentCount = in.readLong();
mLoadedCount = in.readLong();
mUnpluggedCount = in.readLong();
timeBase.add(this);
}
- LongSamplingCounter(TimeBase timeBase) {
+ public LongSamplingCounter(TimeBase timeBase) {
mTimeBase = timeBase;
timeBase.add(this);
}
public void writeToParcel(Parcel out) {
out.writeLong(mCount);
+ out.writeLong(mCurrentCount);
out.writeLong(mLoadedCount);
out.writeLong(mUnpluggedCount);
}
@@ -1598,24 +1602,37 @@
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
+ + " mCurrentCount=" + mCurrentCount
+ " mLoadedCount=" + mLoadedCount
+ " mUnpluggedCount=" + mUnpluggedCount);
}
- void addCountLocked(long count) {
- addCountLocked(count, mTimeBase.isRunning());
+ public void addCountLocked(long count) {
+ update(mCurrentCount + count, mTimeBase.isRunning());
}
- void addCountLocked(long count, boolean isRunning) {
- if (isRunning) {
- mCount += count;
+ public void addCountLocked(long count, boolean isRunning) {
+ update(mCurrentCount + count, isRunning);
+ }
+
+ public void update(long count) {
+ update(count, mTimeBase.isRunning());
+ }
+
+ public void update(long count, boolean isRunning) {
+ if (count < mCurrentCount) {
+ mCurrentCount = 0;
}
+ if (isRunning) {
+ mCount += count - mCurrentCount;
+ }
+ mCurrentCount = count;
}
/**
* Clear state of this counter.
*/
- void reset(boolean detachIfReset) {
+ public void reset(boolean detachIfReset) {
mCount = 0;
mLoadedCount = mUnpluggedCount = 0;
if (detachIfReset) {
@@ -1623,18 +1640,16 @@
}
}
- void detach() {
+ public void detach() {
mTimeBase.remove(this);
}
- void writeSummaryFromParcelLocked(Parcel out) {
+ public void writeSummaryFromParcelLocked(Parcel out) {
out.writeLong(mCount);
}
- void readSummaryFromParcelLocked(Parcel in) {
- mLoadedCount = in.readLong();
- mCount = mLoadedCount;
- mUnpluggedCount = mLoadedCount;
+ public void readSummaryFromParcelLocked(Parcel in) {
+ mCount = mUnpluggedCount= mLoadedCount = in.readLong();
}
}
@@ -11603,10 +11618,6 @@
}
}
- // Cache last value for comparison.
- private BluetoothActivityEnergyInfo mLastBluetoothActivityEnergyInfo =
- new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
-
/**
* Add modem tx power to history
* Device is said to be in high cellular transmit power when it has spent most of the transmit
@@ -11645,8 +11656,35 @@
return;
}
+ private final class BluetoothActivityInfoCache {
+ long idleTimeMs;
+ long rxTimeMs;
+ long txTimeMs;
+ long energy;
+
+ SparseLongArray uidRxBytes = new SparseLongArray();
+ SparseLongArray uidTxBytes = new SparseLongArray();
+
+ void set(BluetoothActivityEnergyInfo info) {
+ idleTimeMs = info.getControllerIdleTimeMillis();
+ rxTimeMs = info.getControllerRxTimeMillis();
+ txTimeMs = info.getControllerTxTimeMillis();
+ energy = info.getControllerEnergyUsed();
+ if (info.getUidTraffic() != null) {
+ for (UidTraffic traffic : info.getUidTraffic()) {
+ uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
+ uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
+ }
+ }
+ }
+ }
+
+ private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
+ = new BluetoothActivityInfoCache();
+
/**
* Distribute Bluetooth energy info and network traffic to apps.
+ *
* @param info The energy information from the bluetooth controller.
*/
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
@@ -11661,12 +11699,13 @@
mHasBluetoothReporting = true;
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long rxTimeMs = info.getControllerRxTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerRxTimeMillis();
- final long txTimeMs = info.getControllerTxTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerTxTimeMillis();
- final long idleTimeMs = info.getControllerIdleTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerIdleTimeMillis();
+ final long rxTimeMs =
+ info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
+ final long txTimeMs =
+ info.getControllerTxTimeMillis() - mLastBluetoothActivityInfo.txTimeMs;
+ final long idleTimeMs =
+ info.getControllerIdleTimeMillis() - mLastBluetoothActivityInfo.idleTimeMs;
+
if (DEBUG_ENERGY) {
Slog.d(TAG, "------ BEGIN BLE power blaming ------");
Slog.d(TAG, " Tx Time: " + txTimeMs + " ms");
@@ -11738,8 +11777,8 @@
}
if (DEBUG_ENERGY) {
- Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs
- + " TX=" + leftOverTxTimeMs);
+ Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs + " TX="
+ + leftOverTxTimeMs);
}
//
@@ -11750,70 +11789,56 @@
long totalRxBytes = 0;
final UidTraffic[] uidTraffic = info.getUidTraffic();
- final UidTraffic[] lastUidTraffic = mLastBluetoothActivityEnergyInfo.getUidTraffic();
- final ArrayList<UidTraffic> deltaTraffic = new ArrayList<>();
- int m = 0, n = 0;
- for (; m < uidTraffic.length && n < lastUidTraffic.length; m++) {
- final UidTraffic traffic = uidTraffic[m];
- final UidTraffic lastTraffic = lastUidTraffic[n];
- if (traffic.getUid() == lastTraffic.getUid()) {
- deltaTraffic.add(new UidTraffic(traffic.getUid(),
- traffic.getRxBytes() - lastTraffic.getRxBytes(),
- traffic.getTxBytes() - lastTraffic.getTxBytes()));
- n++;
- }
- }
- for (; m < uidTraffic.length; m ++) {
- deltaTraffic.add(uidTraffic[m]);
- }
-
- for (int i = 0, j = 0; i < deltaTraffic.size(); i++) {
- final UidTraffic traffic = deltaTraffic.get(i);
+ final int numUids = uidTraffic != null ? uidTraffic.length : 0;
+ for (int i = 0; i < numUids; i++) {
+ final UidTraffic traffic = uidTraffic[i];
+ final long rxBytes = traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(
+ traffic.getUid());
+ final long txBytes = traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(
+ traffic.getUid());
// Add to the global counters.
- mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(
- traffic.getRxBytes());
- mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(
- traffic.getTxBytes());
+ mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(rxBytes);
+ mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(txBytes);
// Add to the UID counters.
final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
- u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, traffic.getRxBytes(), 0);
- u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, traffic.getTxBytes(), 0);
+ u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, rxBytes, 0);
+ u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, txBytes, 0);
// Calculate the total traffic.
- totalTxBytes += traffic.getTxBytes();
- totalRxBytes += traffic.getRxBytes();
+ totalRxBytes += rxBytes;
+ totalTxBytes += txBytes;
}
- if ((totalTxBytes != 0 || totalRxBytes != 0) &&
- (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) {
- for (int i = 0; i < deltaTraffic.size(); i++) {
- final UidTraffic traffic = deltaTraffic.get(i);
+ if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
+ || leftOverTxTimeMs != 0)) {
+ for (int i = 0; i < numUids; i++) {
+ final UidTraffic traffic = uidTraffic[i];
+ final int uid = traffic.getUid();
+ final long rxBytes =
+ traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(uid);
+ final long txBytes =
+ traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(uid);
- final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
+ final Uid u = getUidStatsLocked(mapUid(uid));
final ControllerActivityCounterImpl counter =
u.getOrCreateBluetoothControllerActivityLocked();
- if (totalRxBytes > 0 && traffic.getRxBytes() > 0) {
- final long timeRxMs = (leftOverRxTimeMs * traffic.getRxBytes()) / totalRxBytes;
-
+ if (totalRxBytes > 0 && rxBytes > 0) {
+ final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + traffic.getUid() + " rx_bytes=" + traffic.getRxBytes()
- + " rx_time=" + timeRxMs);
+ Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
}
counter.getRxTimeCounter().addCountLocked(timeRxMs);
leftOverRxTimeMs -= timeRxMs;
}
- if (totalTxBytes > 0 && traffic.getTxBytes() > 0) {
- final long timeTxMs = (leftOverTxTimeMs * traffic.getTxBytes()) / totalTxBytes;
-
+ if (totalTxBytes > 0 && txBytes > 0) {
+ final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + traffic.getUid() + " tx_bytes=" + traffic.getTxBytes()
- + " tx_time=" + timeTxMs);
+ Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
}
-
counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
leftOverTxTimeMs -= timeTxMs;
}
@@ -11830,10 +11855,10 @@
if (opVolt != 0) {
// We store the power drain as mAms.
mBluetoothActivity.getPowerCounter().addCountLocked(
- (long) ((info.getControllerEnergyUsed() -
- mLastBluetoothActivityEnergyInfo.getControllerEnergyUsed() )/ opVolt));
+ (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+ / opVolt));
}
- mLastBluetoothActivityEnergyInfo = info;
+ mLastBluetoothActivityInfo.set(info);
}
/**
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 4901080..f0e7796 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -115,6 +115,14 @@
command.append(' ');
command.append(appProcess);
+ // Generate bare minimum of debug information to be able to backtrace through JITed code.
+ // We assume that if the invoke wrapper is used, backtraces are desirable:
+ // * The wrap.sh script can only be used by debuggable apps, which would enable this flag
+ // without the script anyway (the fork-zygote path). So this makes the two consistent.
+ // * The wrap.* property can only be used on userdebug builds and is likely to be used by
+ // developers (e.g. enable debug-malloc), in which case backtraces are also useful.
+ command.append(" -Xcompiler-option --generate-mini-debug-info");
+
command.append(" /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 933cc7a..577fa17 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -443,7 +443,7 @@
*/
public static int resolveColor(Context context, int color) {
if (color == Notification.COLOR_DEFAULT) {
- return context.getColor(com.android.internal.R.color.notification_icon_default_color);
+ return context.getColor(com.android.internal.R.color.notification_default_color_light);
}
return color;
}
@@ -475,20 +475,15 @@
int backgroundColor, boolean isDark) {
final int resolvedColor = resolveColor(context, notificationColor);
- final int actionBg = context.getColor(
- com.android.internal.R.color.notification_action_list);
-
int color = resolvedColor;
- color = NotificationColorUtil.ensureLargeTextContrast(color, actionBg, isDark);
color = NotificationColorUtil.ensureTextContrast(color, backgroundColor, isDark);
if (color != resolvedColor) {
if (DEBUG){
Log.w(TAG, String.format(
- "Enhanced contrast of notification for %s %s (over action)"
+ "Enhanced contrast of notification for %s"
+ " and %s (over background) by changing #%s to %s",
context.getPackageName(),
- NotificationColorUtil.contrastChange(resolvedColor, color, actionBg),
NotificationColorUtil.contrastChange(resolvedColor, color, backgroundColor),
Integer.toHexString(resolvedColor), Integer.toHexString(color)));
}
@@ -552,6 +547,17 @@
}
}
+ public static int resolveDefaultColor(Context context, int backgroundColor) {
+ boolean useDark = shouldUseDark(backgroundColor);
+ if (useDark) {
+ return context.getColor(
+ com.android.internal.R.color.notification_default_color_light);
+ } else {
+ return context.getColor(
+ com.android.internal.R.color.notification_default_color_dark);
+ }
+ }
+
public static int resolveActionBarColor(Context context, int backgroundColor) {
if (backgroundColor == Notification.COLOR_DEFAULT) {
return context.getColor(com.android.internal.R.color.notification_action_list);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index bec70fd..bff34ca 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -74,7 +74,6 @@
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
- void recoverySecretAvailable(in KeyChainProtectionParams recoverySecret);
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
in List<KeyChainProtectionParams> secrets);
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index e2ce1a4..726c450 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -38,48 +38,81 @@
static jclass gImageDecoder_class;
static jclass gSize_class;
-static jclass gIncomplete_class;
+static jclass gDecodeException_class;
static jclass gCanvas_class;
static jmethodID gImageDecoder_constructorMethodID;
static jmethodID gImageDecoder_postProcessMethodID;
static jmethodID gSize_constructorMethodID;
-static jmethodID gIncomplete_constructorMethodID;
+static jmethodID gDecodeException_constructorMethodID;
static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
+// Clear and return any pending exception for handling other than throwing directly.
+static jthrowable get_and_clear_exception(JNIEnv* env) {
+ jthrowable jexception = env->ExceptionOccurred();
+ if (jexception) {
+ env->ExceptionClear();
+ }
+ return jexception;
+}
+
+// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
+static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const char* msg,
+ jthrowable cause, jobject source) {
+ jstring jstr = nullptr;
+ if (msg) {
+ jstr = env->NewStringUTF(msg);
+ if (!jstr) {
+ // Out of memory.
+ return nullptr;
+ }
+ }
+ jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class,
+ gDecodeException_constructorMethodID, error, jstr, cause, source);
+ // Only throw if not out of memory.
+ if (exception) {
+ env->Throw(exception);
+ }
+ return nullptr;
+}
+
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
if (!stream.get()) {
- doThrowIOE(env, "Failed to create a stream");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+ nullptr, source);
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
SkCodec::Result result;
auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+ if (jthrowable jexception = get_and_clear_exception(env)) {
+ return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+ }
if (!codec) {
switch (result) {
case SkCodec::kIncompleteInput:
- env->ThrowNew(gIncomplete_class, "Incomplete input");
- break;
+ return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source);
default:
SkString msg;
msg.printf("Failed to create image decoder with message '%s'",
SkCodec::ResultToString(result));
- doThrowIOE(env, msg.c_str());
- break;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, msg.c_str(),
+ nullptr, source);
}
- return nullptr;
}
- // FIXME: Avoid parsing the whole image?
const bool animated = codec->getFrameCount() > 1;
+ if (jthrowable jexception = get_and_clear_exception(env)) {
+ return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+ }
+
decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
SkAndroidCodec::ExifOrientationBehavior::kRespect);
if (!decoder->mCodec.get()) {
- doThrowIOE(env, "Could not create AndroidCodec");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, "", nullptr, source);
}
+
const auto& info = decoder->mCodec->getInfo();
const int width = info.width();
const int height = info.height();
@@ -89,26 +122,26 @@
}
static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
- jobject fileDescriptor) {
+ jobject fileDescriptor, jobject source) {
int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
struct stat fdStat;
if (fstat(descriptor, &fdStat) == -1) {
- doThrowIOE(env, "broken file descriptor; fstat returned -1");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData,
+ "broken file descriptor; fstat returned -1", nullptr, source);
}
int dupDescriptor = dup(descriptor);
FILE* file = fdopen(dupDescriptor, "r");
if (file == NULL) {
close(dupDescriptor);
- doThrowIOE(env, "Could not open file");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file",
+ nullptr, source);
}
std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
- return native_create(env, std::move(fileStream));
+ return native_create(env, std::move(fileStream), source);
}
// FIXME: This allows us to pretend the current location is the beginning,
@@ -116,44 +149,46 @@
// point as the beginning.
std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream),
SkCodec::MinBufferedBytesNeeded()));
- return native_create(env, std::move(stream));
+ return native_create(env, std::move(stream), source);
}
static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
- jobject is, jbyteArray storage) {
+ jobject is, jbyteArray storage, jobject source) {
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
if (!stream.get()) {
- doThrowIOE(env, "Failed to create stream!");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+ nullptr, source);
}
+
std::unique_ptr<SkStream> bufferedStream(
SkFrontBufferedStream::Make(std::move(stream),
SkCodec::MinBufferedBytesNeeded()));
- return native_create(env, std::move(bufferedStream));
+ return native_create(env, std::move(bufferedStream), source);
}
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
+ jobject source) {
Asset* asset = reinterpret_cast<Asset*>(assetPtr);
std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
- return native_create(env, std::move(stream));
+ return native_create(env, std::move(stream), source);
}
static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
- jint initialPosition, jint limit) {
+ jint initialPosition, jint limit, jobject source) {
std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
initialPosition, limit);
if (!stream) {
- doThrowIOE(env, "Failed to read ByteBuffer");
- return nullptr;
+ return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
+ nullptr, source);
}
- return native_create(env, std::move(stream));
+ return native_create(env, std::move(stream), source);
}
static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
- jint offset, jint length) {
+ jint offset, jint length, jobject source) {
std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
- return native_create(env, std::move(stream));
+ return native_create(env, std::move(stream), source);
}
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -170,10 +205,8 @@
return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
}
-// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
-// otherwise.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
- jobject jcallback, jobject jpostProcess,
+ jobject jdecoder, jboolean jpostProcess,
jint desiredWidth, jint desiredHeight, jobject jsubset,
jboolean requireMutable, jint allocator,
jboolean requireUnpremul, jboolean preferRamOverQuality,
@@ -264,11 +297,8 @@
SkAndroidCodec::AndroidOptions options;
options.fSampleSize = sampleSize;
auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
- jthrowable jexception = env->ExceptionOccurred();
- if (jexception) {
- env->ExceptionClear();
- }
- int onPartialImageError = jexception ? 1 // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+ jthrowable jexception = get_and_clear_exception(env);
+ int onPartialImageError = jexception ? ImageDecoder::kSourceException
: 0; // No error.
switch (result) {
case SkCodec::kSuccess:
@@ -278,12 +308,12 @@
break;
case SkCodec::kIncompleteInput:
if (!jexception) {
- onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+ onPartialImageError = ImageDecoder::kSourceIncomplete;
}
break;
case SkCodec::kErrorInInput:
if (!jexception) {
- onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR
+ onPartialImageError = ImageDecoder::kSourceMalformedData;
}
break;
default:
@@ -293,24 +323,12 @@
return nullptr;
}
- if (jexception || onPartialImageError) {
- bool throwException = !jcallback ||
- !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
- onPartialImageError);
+ if (onPartialImageError) {
+ env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError,
+ jexception);
if (env->ExceptionCheck()) {
return nullptr;
}
-
- if (throwException) {
- if (jexception) {
- env->Throw(jexception);
- } else if (onPartialImageError == 2) {
- env->ThrowNew(gIncomplete_class, "Incomplete input");
- } else {
- doThrowIOE(env, "image has an error!");
- }
- return nullptr;
- }
}
float scaleX = 1.0f;
@@ -357,11 +375,6 @@
SkIRect subset;
GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
- // FIXME: If there is no scale, should this instead call
- // SkBitmap::extractSubset? If we could upload a subset
- // (b/70626068), this would save memory and time. Even for a
- // software Bitmap, the extra speed might be worth the memory
- // tradeoff if the subset is large?
translateX = -subset.fLeft;
translateY = -subset.fTop;
desiredWidth = subset.width();
@@ -404,7 +417,7 @@
if (jpostProcess) {
std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
- jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas));
+ jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas));
if (env->ExceptionCheck()) {
return nullptr;
}
@@ -495,12 +508,12 @@
}
static const JNINativeMethod gImageDecoderMethods[] = {
- { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
- { "nCreate", "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
- { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
- { "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
- { "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
- { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+ { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
+ { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+ { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+ { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+ { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
@@ -516,10 +529,10 @@
gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
- gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException"));
- gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V");
+ gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException"));
+ gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V");
- gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(I)Z");
+ gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V");
gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h
index 5d7e676..fd9827b 100644
--- a/core/jni/android/graphics/ImageDecoder.h
+++ b/core/jni/android/graphics/ImageDecoder.h
@@ -33,6 +33,13 @@
kHardware_Allocator = 3,
};
+ // These need to stay in sync with ImageDecoder.java's Error constants.
+ enum Error {
+ kSourceException = 1,
+ kSourceIncomplete = 2,
+ kSourceMalformedData = 3,
+ };
+
// These need to stay in sync with PixelFormat.java's Format constants.
enum PixelFormat {
kUnknown = 0,
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 0017f6c..c9bfa13 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -18,6 +18,7 @@
#include "GraphicsJNI.h"
#include "core_jni_helpers.h"
+#include <android/api-level.h>
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
@@ -467,6 +468,13 @@
static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
jint meshWidth, jint meshHeight, jfloatArray jverts,
jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+ if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
+ // Before P we forgot to respect these. Now that we do respect them, explicitly
+ // zero them for backward compatibility.
+ vertIndex = 0;
+ colorIndex = 0;
+ }
+
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
@@ -474,7 +482,8 @@
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
- vertA.ptr(), colorA.ptr(), paint);
+ vertA.ptr() + vertIndex*2,
+ colorA.ptr() + colorIndex, paint);
}
static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index d33337d..9d79417 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "MeasuredParagraph"
+#include "GraphicsJNI.h"
#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
@@ -109,6 +110,33 @@
return r;
}
+// Regular JNI
+static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jlong paintPtr,
+ jint start, jint end, jint bidiFlags, jobject bounds) {
+ ScopedCharArrayRO text(env, javaText);
+ const minikin::U16StringPiece textBuffer(text.get(), text.size());
+
+ minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
+ Paint* paint = toPaint(paintPtr);
+ const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
+ minikin::Layout layout = MinikinUtils::doLayout(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface, textBuffer.data(), start, end - start,
+ textBuffer.size(), mt);
+
+ minikin::MinikinRect rect;
+ layout.getBounds(&rect);
+
+ SkRect r;
+ r.fLeft = rect.mLeft;
+ r.fTop = rect.mTop;
+ r.fRight = rect.mRight;
+ r.fBottom = rect.mBottom;
+
+ SkIRect ir;
+ r.roundOut(&ir);
+ GraphicsJNI::irect_to_jrect(ir, env, bounds);
+}
+
// CriticalNative
static jlong nGetReleaseFunc() {
return toJLong(&releaseMeasuredParagraph);
@@ -128,6 +156,7 @@
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
+ {"nGetBounds", "(J[CJIIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7c8a52d..b5fd792 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -666,7 +666,9 @@
}
}
- if (!is_system_server) {
+ // If this zygote isn't root, it won't be able to create a process group,
+ // since the directory is owned by root.
+ if (!is_system_server && getuid() == 0) {
int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
if (rc == -EROFS) {
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 4bb9707..89665db 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -228,6 +228,7 @@
optional SettingProto ble_scan_low_power_interval_ms = 161 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto ble_scan_balanced_interval_ms = 162 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto ble_scan_low_latency_interval_ms = 163 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto ble_scan_background_mode = 389 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_saved_state = 164 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_supplicant_scan_interval_ms = 165 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_enhanced_auto_join = 166 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -507,7 +508,7 @@
optional SettingsProto backup_agent_timeout_parameters = 386;
// Please insert fields in the same order as in
// frameworks/base/core/java/android/provider/Settings.java.
- // Next tag = 389;
+ // Next tag = 390;
}
message SecureSettingsProto {
@@ -744,9 +745,10 @@
optional SettingProto backup_manager_constants = 193;
optional SettingProto backup_local_transport_parameters = 194;
optional SettingProto bluetooth_on_while_driving = 195 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingsProto volume_hush_gesture = 196 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Please insert fields in the same order as in
// frameworks/base/core/java/android/provider/Settings.java.
- // Next tag = 196
+ // Next tag = 197
}
message SystemSettingsProto {
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index f8050a1..88bb4a6 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -110,6 +110,7 @@
optional bool is_launched = 6;
optional EnabledState enabled_state = 7;
optional string last_disabled_app_caller = 8;
+ optional string suspending_package = 9;
}
// Name of package. e.g. "com.android.providers.telephony".
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1b4d571..1924bac 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1341,6 +1341,13 @@
android:label="@string/permlab_changeWifiState"
android:protectionLevel="normal" />
+ <!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
+ <p>Only granted to applications that are currently bound by the
+ system for creating and managing IPsec-based interfaces.
+ -->
+ <permission android:name="android.permission.MANAGE_IPSEC_TUNNELS"
+ android:protectionLevel="signature|appop" />
+
<!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_WIFI_CREDENTIAL"
@@ -1441,6 +1448,13 @@
android:label="@string/permlab_bluetooth"
android:protectionLevel="normal" />
+ <!-- @SystemApi Allows an application to suspend other apps, which will prevent the user
+ from using them until they are unsuspended.
+ @hide
+ -->
+ <permission android:name="android.permission.SUSPEND_APPS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
-->
@@ -3310,8 +3324,9 @@
<permission android:name="android.permission.BACKUP"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows application to manage RecoverableKeyStoreLoader.
- <p>Not for use by third-party applications.
+ <!-- @SystemApi Allows application to manage
+ {@link android.security.keystore.recovery.RecoveryController}.
+ <p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.RECOVER_KEYSTORE"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 75b3bcf..3135455 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5968,6 +5968,9 @@
in the encoded data. Setting this to infinite (-1) will result in the
animation repeating as long as it is displayed (once start() is called). -->
<attr name="repeatCount"/>
+ <!-- When true, automatically start animating. The default is false, meaning
+ that the animation will not start until start() is called. -->
+ <attr name="autoStart" />
</declare-styleable>
<!-- Drawable used to draw bitmaps. -->
@@ -7984,8 +7987,6 @@
android.content.pm.PackageInfo#getLongVersionCode()} for the target package.
-->
<attr name="maxLongVersionCode" format="string" />
- <!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
- <attr name="urlBarResourceId" format="string" />
</declare-styleable>
<!-- =============================== -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 722102e..449d3e7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,13 +130,14 @@
<drawable name="notification_template_divider_media">#29ffffff</drawable>
<color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color>
<color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color>
- <color name="notification_secondary_text_color_light">@color/secondary_text_material_light</color>
- <color name="notification_secondary_text_color_dark">@color/secondary_text_material_dark</color>
+ <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color>
+ <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color>
+ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
+ <color name="notification_default_color_light">#a3202124</color>
<color name="notification_material_background_color">#ffffffff</color>
<color name="notification_default_color">#757575</color> <!-- Gray 600 -->
- <color name="notification_icon_default_color">@color/notification_default_color</color>
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 3609fb8..6e8134b 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -97,7 +97,7 @@
<color name="material_deep_teal_100">#ffb2dfdb</color>
<color name="material_deep_teal_200">#ff80cbc4</color>
<color name="material_deep_teal_300">#ff4db6ac</color>
- <color name="material_deep_teal_500">#ff008577</color>
+ <color name="material_deep_teal_500">#ff009688</color>
<color name="material_blue_grey_200">#ffb0bec5</color>
<color name="material_blue_grey_700">#ff455a64</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bbb0b0a..55c17b9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2911,6 +2911,10 @@
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
+ <!-- Allow the gesture power + volume up to change the ringer mode while the device
+ is interactive. -->
+ <bool name="config_volumeHushGestureEnabled">true</bool>
+
<!-- Name of the component to handle network policy notifications. If present,
disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
<string translatable="false" name="config_networkPolicyNotificationComponent"></string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2c0deed..0246c80 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2869,8 +2869,6 @@
<public name="outlineSpotShadowColor" />
<public name="outlineAmbientShadowColor" />
<public name="maxLongVersionCode" />
- <!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
- <public name="urlBarResourceId" />
<!-- @hide @SystemApi -->
<public name="userRestriction" />
<public name="textFontWeight" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f0f7270..7038887 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -159,6 +159,9 @@
<!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
<string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
+ <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. -->
+ <string name="RestrictedStateContentMsimTemplate">Temporarily turned off by your carrier for SIM <xliff:g id="simNumber" example="1">%d</xliff:g></string>
+
<!-- Displayed to tell the user that they should switch their network preference. -->
<string name="NetworkPreferenceSwitchTitle">Can\u2019t reach mobile network</string>
<!-- Displayed to tell the user that they should switch their network preference. -->
@@ -227,14 +230,14 @@
<string name="roamingTextSearching">Searching for Service</string>
<!-- Displayed when WFC registration fails -->
- <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
+ <string name="wfcRegErrorTitle">Couldn\u2019t set up Wi\u2011Fi calling</string>
<!-- WFC Operator Error Messages showed as alerts -->
<string-array name="wfcOperatorErrorAlertMessages">
<item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
</string-array>
<!-- WFC Operator Error Messages showed as notifications -->
<string-array name="wfcOperatorErrorNotificationMessages">
- <item>Register with your carrier (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
+ <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
</string-array>
<!-- Template for showing mobile network operator name while WFC is active -->
<string-array name="wfcSpnFormats">
@@ -3085,13 +3088,13 @@
<string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
<!-- Notification title for a nearby carrier wireless network.-->
<string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
- <!-- Notification title when the system is connecting to the specified open network. The network name is specified in the notification content. -->
- <string name="wifi_available_title_connecting">Connecting to open Wi\u2011Fi network</string>
- <!-- Notification title when the system has connected to the open network. The network name is specified in the notification content. -->
+ <!-- Notification title when the system is connecting to the specified network. The network name is specified in the notification content. -->
+ <string name="wifi_available_title_connecting">Connecting to Wi\u2011Fi network</string>
+ <!-- Notification title when the system has connected to the network. The network name is specified in the notification content. -->
<string name="wifi_available_title_connected">Connected to Wi\u2011Fi network</string>
- <!-- Notification title when the system failed to connect to the specified open network. -->
+ <!-- Notification title when the system failed to connect to the specified network. -->
<string name="wifi_available_title_failed_to_connect">Could not connect to Wi\u2011Fi network</string>
- <!-- Notification content when the system failed to connect to the specified open network. This informs the user that tapping on this notification will open the wifi picker. -->
+ <!-- Notification content when the system failed to connect to the specified network. This informs the user that tapping on this notification will open the wifi picker. -->
<string name="wifi_available_content_failed_to_connect">Tap to see all networks</string>
<!-- Notification action name for connecting to the network specified in the notification body. -->
<string name="wifi_available_action_connect">Connect</string>
@@ -4833,6 +4836,12 @@
<string name="mmcc_illegal_ms">SIM not allowed for voice</string>
<string name="mmcc_illegal_me">Phone not allowed for voice</string>
+ <!-- Title of notification when UE fails to register network with MM reject cause code when multiple SIMs are active. -->
+ <string name="mmcc_authentication_reject_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+ <string name="mmcc_imsi_unknown_in_hlr_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not provisioned</string>
+ <string name="mmcc_illegal_ms_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+ <string name="mmcc_illegal_me_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+
<!-- Popup window default title to be read by a screen reader-->
<string name="popup_window_default_title">Popup Window</string>
@@ -4888,6 +4897,9 @@
<!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
<string name="screenshot_edit">Edit</string>
+ <string name="volume_dialog_ringer_guidance_vibrate">Calls and notifications will vibrate</string>
+ <string name="volume_dialog_ringer_guidance_silent">Calls and notifications will be muted</string>
+
<!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
<string name="notification_channel_system_changes">System changes</string>
<!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index fa3cf2f..b8a046f 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -473,7 +473,7 @@
<style name="TextAppearance.Material.Notification.Reply" />
<style name="TextAppearance.Material.Notification.Title">
- <item name="textColor">@color/notification_primary_text_color_light</item>
+ <item name="fontFamily">sans-serif-medium</item>
<item name="textSize">@dimen/notification_title_text_size</item>
</style>
@@ -482,7 +482,6 @@
</style>
<style name="TextAppearance.Material.Notification.Info">
- <item name="textColor">@color/notification_secondary_text_color_light</item>
<item name="textSize">@dimen/notification_subtext_size</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4cf50f5..3bf97d0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -579,6 +579,7 @@
<java-symbol type="string" name="RestrictedOnEmergencyTitle" />
<java-symbol type="string" name="RestrictedOnNormalTitle" />
<java-symbol type="string" name="RestrictedStateContent" />
+ <java-symbol type="string" name="RestrictedStateContentMsimTemplate" />
<java-symbol type="string" name="notification_channel_network_alert" />
<java-symbol type="string" name="notification_channel_call_forward" />
<java-symbol type="string" name="notification_channel_emergency_callback" />
@@ -1024,6 +1025,8 @@
<java-symbol type="string" name="volume_icon_description_media" />
<java-symbol type="string" name="volume_icon_description_notification" />
<java-symbol type="string" name="volume_icon_description_ringer" />
+ <java-symbol type="string" name="volume_dialog_ringer_guidance_vibrate" />
+ <java-symbol type="string" name="volume_dialog_ringer_guidance_silent" />
<java-symbol type="string" name="wait" />
<java-symbol type="string" name="webpage_unresponsive" />
<java-symbol type="string" name="whichApplication" />
@@ -2029,6 +2032,10 @@
<java-symbol type="string" name="mmcc_imsi_unknown_in_hlr" />
<java-symbol type="string" name="mmcc_illegal_ms" />
<java-symbol type="string" name="mmcc_illegal_me" />
+ <java-symbol type="string" name="mmcc_authentication_reject_msim_template" />
+ <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr_msim_template" />
+ <java-symbol type="string" name="mmcc_illegal_ms_msim_template" />
+ <java-symbol type="string" name="mmcc_illegal_me_msim_template" />
<java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="vr_listener_binding_label" />
<java-symbol type="string" name="condition_provider_service_binding_label" />
@@ -2115,7 +2122,6 @@
<java-symbol type="layout" name="notification_template_material_big_text" />
<java-symbol type="layout" name="notification_template_header" />
<java-symbol type="layout" name="notification_material_media_action" />
- <java-symbol type="color" name="notification_icon_default_color" />
<java-symbol type="color" name="notification_progress_background_color" />
<java-symbol type="id" name="media_actions" />
@@ -2621,6 +2627,7 @@
<java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
+ <java-symbol type="bool" name="config_volumeHushGestureEnabled" />
<java-symbol type="drawable" name="platlogo_m" />
@@ -2964,6 +2971,8 @@
<java-symbol type="color" name="notification_primary_text_color_dark" />
<java-symbol type="color" name="notification_secondary_text_color_light" />
<java-symbol type="color" name="notification_secondary_text_color_dark" />
+ <java-symbol type="color" name="notification_default_color_light" />
+ <java-symbol type="color" name="notification_default_color_dark" />
<java-symbol type="string" name="app_category_game" />
<java-symbol type="string" name="app_category_audio" />
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index be3a0be..2fe7db4 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -31,6 +31,11 @@
import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan;
import android.util.Patterns;
+import android.view.textclassifier.SystemTextClassifier;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierImpl;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
import android.widget.TextView;
import org.junit.After;
@@ -38,7 +43,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* LinkifyTest tests {@link Linkify}.
@@ -149,4 +158,55 @@
assertEquals("android.com should be linkified", 1, spans.length);
assertEquals("https://android.com", spans[0].getURL());
}
+
+ @Test
+ public void testAddLinksAsync_useLegacyIfSmartDisabled_localTextClassifier()
+ throws Exception {
+ final TextClassificationConstants settings =
+ TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
+ final TextClassifier classifier = new TextClassifierImpl(mContext, settings);
+ testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
+ }
+
+ @Test
+ public void testAddLinksAsync_useLegacyIfSmartDisabled_systemTextClassifier()
+ throws Exception {
+ final TextClassificationConstants settings =
+ TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
+ final TextClassifier classifier = new SystemTextClassifier(mContext, settings);
+ testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
+ }
+
+ private void testAddLinksAsync_useLegacyIfSmartDisabled(TextClassifier classifier)
+ throws Exception {
+ final int linkMask = Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS;
+ final String string = "example@android.com\n"
+ + "(415) 555-1212\n"
+ + "http://android.com\n"
+ + "100 Android Rd California";
+ final Spannable expected = new SpannableString(string);
+ final Spannable actual = new SpannableString(string);
+
+ Linkify.addLinks(expected, linkMask); // legacy.
+ Linkify.addLinksAsync(actual, classifier, linkMask).get();
+
+ final URLSpan[] expectedSpans = expected.getSpans(0, expected.length(), URLSpan.class);
+ final TextLinkSpan[] actualSpans = actual.getSpans(0, actual.length(), TextLinkSpan.class);
+ assertEquals(expectedSpans.length, actualSpans.length);
+ final Set<List> expectedLinkSpec = new HashSet<>();
+ final Set<List> actualLinkSpec = new HashSet<>();
+ for (int i = 0; i < expectedSpans.length; i++) {
+ final URLSpan expectedSpan = expectedSpans[i];
+ final TextLinkSpan actualSpan = actualSpans[i];
+ expectedLinkSpec.add(Arrays.asList(
+ expected.getSpanStart(expectedSpan),
+ expected.getSpanEnd(expectedSpan),
+ expectedSpan.getURL()));
+ actualLinkSpec.add(Arrays.asList(
+ actual.getSpanStart(actualSpan),
+ actual.getSpanEnd(actualSpan),
+ actualSpan.getUrl()));
+ }
+ assertEquals(expectedLinkSpec, actualLinkSpec);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index f5fe80c..94492ba 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -43,6 +43,7 @@
KernelUidCpuActiveTimeReaderTest.class,
KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
+ LongSamplingCounterTest.class,
LongSamplingCounterArrayTest.class,
PowerCalculatorTest.class,
PowerProfileTest.class
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
new file mode 100644
index 0000000..853bf8a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.os;
+
+import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+
+import static com.android.internal.os.BatteryStatsImpl.LongSamplingCounter;
+import static com.android.internal.os.BatteryStatsImpl.TimeBase;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link LongSamplingCounter}.
+ *
+ * To run the tests, use
+ *
+ * bit FrameworksCoreTests:com.android.internal.os.LongSamplingCounterTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LongSamplingCounterTest {
+
+ private static final long COUNT = 1111;
+ private static final long CURRENT_COUNT = 5555;
+
+ @Mock
+ private TimeBase mTimeBase;
+ private LongSamplingCounter mCounter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCounter = new LongSamplingCounter(mTimeBase);
+ Mockito.reset(mTimeBase);
+ }
+
+ @Test
+ public void testReadWriteParcel() {
+ final Parcel parcel = Parcel.obtain();
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.writeToParcel(parcel);
+ parcel.setDataPosition(0);
+
+ // Now clear counterArray and verify values are read from parcel correctly.
+ updateCounts(0, 0);
+
+ mCounter = new LongSamplingCounter(mTimeBase, parcel);
+ assertEquals(COUNT, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testReadWriteSummaryParcel() {
+ final Parcel parcel = Parcel.obtain();
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.writeSummaryFromParcelLocked(parcel);
+ parcel.setDataPosition(0);
+
+ // Now clear counterArray and verify values are read from parcel correctly.
+ updateCounts(0, 0);
+
+ mCounter.readSummaryFromParcelLocked(parcel);
+ assertEquals(COUNT, mCounter.mCount);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testOnTimeStarted() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.onTimeStarted(0, 0, 0);
+ assertEquals(COUNT, mCounter.mCount);
+ assertEquals(COUNT, mCounter.mUnpluggedCount);
+ }
+
+ @Test
+ public void testOnTimeStopped() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.onTimeStopped(0, 0, 0);
+ assertEquals(COUNT, mCounter.mCount);
+ }
+
+ @Test
+ public void testAddCountLocked() {
+ updateCounts(0, 0);
+ assertEquals(0, mCounter.getCountLocked(0));
+ when(mTimeBase.isRunning()).thenReturn(true);
+ mCounter.addCountLocked(111);
+ assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(111, mCounter.mCurrentCount);
+ mCounter.addCountLocked(222);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(333, mCounter.mCurrentCount);
+
+ when(mTimeBase.isRunning()).thenReturn(false);
+ mCounter.addCountLocked(456);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(789, mCounter.mCurrentCount);
+
+ mCounter.addCountLocked(444, true);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1233, mCounter.mCurrentCount);
+ mCounter.addCountLocked(567, false);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1800, mCounter.mCurrentCount);
+ }
+
+ @Test
+ public void testUpdate() {
+ updateCounts(0, 0);
+ assertEquals(0, mCounter.getCountLocked(0));
+ when(mTimeBase.isRunning()).thenReturn(true);
+ mCounter.update(111);
+ assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(111, mCounter.mCurrentCount);
+ mCounter.update(333);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(333, mCounter.mCurrentCount);
+
+ when(mTimeBase.isRunning()).thenReturn(false);
+ mCounter.update(789);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(789, mCounter.mCurrentCount);
+ mCounter.update(100);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(100, mCounter.mCurrentCount);
+
+ mCounter.update(544, true);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(544, mCounter.mCurrentCount);
+ mCounter.update(1544, false);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1544, mCounter.mCurrentCount);
+ }
+
+ @Test
+ public void testReset() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ // Test with detachIfReset=false
+ mCounter.reset(false /* detachIfReset */);
+ assertEquals(0, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ verifyZeroInteractions(mTimeBase);
+
+ updateCounts(COUNT, CURRENT_COUNT);
+ // Test with detachIfReset=true
+ mCounter.reset(true /* detachIfReset */);
+ assertEquals(0, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ verify(mTimeBase).remove(mCounter);
+ verifyNoMoreInteractions(mTimeBase);
+ }
+
+ @Test
+ public void testDetach() {
+ mCounter.detach();
+ verify(mTimeBase).remove(mCounter);
+ verifyNoMoreInteractions(mTimeBase);
+ }
+
+ private void updateCounts(long total, long current) {
+ mCounter.mCount = total;
+ mCounter.mCurrentCount = current;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 002df88..518cadd 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -16,7 +16,6 @@
package com.android.internal.policy;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -27,7 +26,6 @@
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.ActionMode;
@@ -44,7 +42,6 @@
*/
@SmallTest
@Presubmit
-@FlakyTest(detail = "Promote to presubmit once monitored to be stable.")
@RunWith(AndroidJUnit4.class)
public final class PhoneWindowTest {
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 374d0d0..3c1526b 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 6fe19a2..c88a722 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -16,6 +16,7 @@
package android.privacy;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -72,27 +73,27 @@
final LongitudinalReportingEncoder encoder =
LongitudinalReportingEncoder.createInsecureEncoderForTest(
config);
- assertEquals(0, encoder.encodeBoolean(true)[0]);
- assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
- assertEquals(1, encoder.encodeBoolean(true)[0]);
- assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
- assertEquals(1, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
// Test if IRR returns original result when f = 0
final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
@@ -128,6 +129,31 @@
}
@Test
+ public void testLongitudinalReportingInsecureEncoder_setSeedCorrectly() throws Exception {
+ final int n = 10000;
+ final double f = 0.35;
+ final double expectedTrueSum = n * f;
+ final double valueRange = 5 * Math.sqrt(n * f * (1 - f));
+ int trueSum = 0;
+ for (int i = 0; i < n; i++) {
+ final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
+ "encoder" + i, // encoderId
+ f, // probabilityF
+ 0, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder
+ = LongitudinalReportingEncoder.createInsecureEncoderForTest(config);
+ boolean encodedFalse = encoder.encodeBoolean(false)[0] > 0;
+ if (encodedFalse) {
+ trueSum += 1;
+ }
+ }
+ // Total number of true(s) should be around the mean (10000 * 0.35)
+ assertThat((double) trueSum).isLessThan(expectedTrueSum + valueRange);
+ assertThat((double) trueSum).isAtLeast(expectedTrueSum - valueRange);
+ }
+
+ @Test
public void testLongitudinalReportingEncoder_basicPRRTest() throws Exception {
// Should always return original value when p = 0
for (int i = 0; i < 10; i++) {
@@ -290,8 +316,8 @@
}
}
// Total number of true(s) should be around the mean (1000 * 0.8)
- assertTrue(trueSum1 < expectedTrueSum1 + valueRange1);
- assertTrue(trueSum1 > expectedTrueSum1 - valueRange1);
+ assertThat((double) trueSum1).isLessThan(expectedTrueSum1 + valueRange1);
+ assertThat((double) trueSum1).isAtLeast(expectedTrueSum1 - valueRange1);
// Confirm if PRR randomizer is working correctly
final int n2 = 1000;
@@ -314,8 +340,8 @@
}
}
// Total number of true(s) should be around the mean (1000 * 0.2)
- assertTrue(trueSum2 < expectedTrueSum2 + valueRange2);
- assertTrue(trueSum2 > expectedTrueSum2 - valueRange2);
+ assertThat((double) trueSum2).isLessThan(expectedTrueSum2 + valueRange2);
+ assertThat((double) trueSum2).isAtLeast(expectedTrueSum2 - valueRange2);
}
@Test
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index fa0343d..71bd8f1 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -79,11 +79,11 @@
public void testRapporEncoder_IRRWithPRR() throws Exception {
int numBits = 8;
final long inputValue = 254L;
- final long prrValue = 250L;
- final long prrAndIrrValue = 244L;
+ final long expectedPrrValue = 126L;
+ final long expectedPrrAndIrrValue = 79L;
final RapporConfig config1 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0, // probabilityP
@@ -93,12 +93,12 @@
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder1 = RapporEncoder.createInsecureEncoderForTest(config1);
// Verify that PRR is working as expected.
- assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
+ assertEquals(expectedPrrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
assertTrue(encoder1.isInsecureEncoderForTest());
// Verify that IRR is working as expected.
final RapporConfig config2 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0, // probabilityF
0.3, // probabilityP
@@ -107,11 +107,12 @@
2); // numBloomHashes
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder2 = RapporEncoder.createInsecureEncoderForTest(config2);
- assertEquals(prrAndIrrValue, toLong(encoder2.encodeBits(toBytes(prrValue))));
+ assertEquals(expectedPrrAndIrrValue,
+ toLong(encoder2.encodeBits(toBytes(expectedPrrValue))));
// Test that end-to-end is the result of PRR + IRR.
final RapporConfig config3 = new RapporConfig(
- "Foo", // encoderId
+ "Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0.3, // probabilityP
@@ -120,7 +121,7 @@
2); // numBloomHashes
final RapporEncoder encoder3 = RapporEncoder.createInsecureEncoderForTest(config3);
// Verify that PRR is working as expected.
- assertEquals(prrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
+ assertEquals(expectedPrrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
}
@Test
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d925441..b0bc102 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1490,6 +1490,10 @@
* across the top of the bitmap from left to right. A more general version of this method is
* drawVertices().
*
+ * Prior to API level {@value Build.VERSION_CODES#P} vertOffset and colorOffset were ignored,
+ * effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
+ * these parameters will be respected.
+ *
* @param bitmap The bitmap to draw using the mesh
* @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
* @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 2f09c65..5ca0ad6 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -24,6 +24,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
@@ -102,7 +103,7 @@
@Override
public ImageDecoder createImageDecoder() throws IOException {
- return nCreate(mData, mOffset, mLength);
+ return nCreate(mData, mOffset, mLength, this);
}
}
@@ -117,9 +118,9 @@
if (!mBuffer.isDirect() && mBuffer.hasArray()) {
int offset = mBuffer.arrayOffset() + mBuffer.position();
int length = mBuffer.limit() - mBuffer.position();
- return nCreate(mBuffer.array(), offset, length);
+ return nCreate(mBuffer.array(), offset, length, this);
}
- return nCreate(mBuffer, mBuffer.position(), mBuffer.limit());
+ return nCreate(mBuffer, mBuffer.position(), mBuffer.limit(), this);
}
}
@@ -156,7 +157,7 @@
throw new FileNotFoundException(mUri.toString());
}
- return createFromStream(is, true);
+ return createFromStream(is, true, this);
}
final FileDescriptor fd = assetFd.getFileDescriptor();
@@ -166,9 +167,9 @@
try {
try {
Os.lseek(fd, offset, SEEK_SET);
- decoder = nCreate(fd);
+ decoder = nCreate(fd, this);
} catch (ErrnoException e) {
- decoder = createFromStream(new FileInputStream(fd), true);
+ decoder = createFromStream(new FileInputStream(fd), true, this);
}
} finally {
if (decoder == null) {
@@ -182,18 +183,19 @@
}
@NonNull
- private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
+ private static ImageDecoder createFromFile(@NonNull File file,
+ @NonNull Source source) throws IOException {
FileInputStream stream = new FileInputStream(file);
FileDescriptor fd = stream.getFD();
try {
Os.lseek(fd, 0, SEEK_CUR);
} catch (ErrnoException e) {
- return createFromStream(stream, true);
+ return createFromStream(stream, true, source);
}
ImageDecoder decoder = null;
try {
- decoder = nCreate(fd);
+ decoder = nCreate(fd, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(stream);
@@ -207,12 +209,12 @@
@NonNull
private static ImageDecoder createFromStream(@NonNull InputStream is,
- boolean closeInputStream) throws IOException {
+ boolean closeInputStream, Source source) throws IOException {
// Arbitrary size matches BitmapFactory.
byte[] storage = new byte[16 * 1024];
ImageDecoder decoder = null;
try {
- decoder = nCreate(is, storage);
+ decoder = nCreate(is, storage, source);
} finally {
if (decoder == null) {
if (closeInputStream) {
@@ -260,7 +262,7 @@
}
InputStream is = mInputStream;
mInputStream = null;
- return createFromStream(is, false);
+ return createFromStream(is, false, this);
}
}
}
@@ -305,7 +307,7 @@
}
AssetInputStream ais = mAssetInputStream;
mAssetInputStream = null;
- return createFromAsset(ais);
+ return createFromAsset(ais, this);
}
}
}
@@ -340,18 +342,19 @@
mResDensity = value.density;
}
- return createFromAsset((AssetInputStream) is);
+ return createFromAsset((AssetInputStream) is, this);
}
}
/**
* ImageDecoder will own the AssetInputStream.
*/
- private static ImageDecoder createFromAsset(AssetInputStream ais) throws IOException {
+ private static ImageDecoder createFromAsset(AssetInputStream ais,
+ Source source) throws IOException {
ImageDecoder decoder = null;
try {
long asset = ais.getNativeAsset();
- decoder = nCreate(asset);
+ decoder = nCreate(asset, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(ais);
@@ -375,7 +378,7 @@
@Override
public ImageDecoder createImageDecoder() throws IOException {
InputStream is = mAssets.open(mFileName);
- return createFromAsset((AssetInputStream) is);
+ return createFromAsset((AssetInputStream) is, this);
}
}
@@ -388,7 +391,7 @@
@Override
public ImageDecoder createImageDecoder() throws IOException {
- return createFromFile(mFile);
+ return createFromFile(mFile, this);
}
}
@@ -431,9 +434,10 @@
}
};
- /**
- * Thrown if the provided data is incomplete.
+ /** @removed
+ * @deprecated Subsumed by {@link #DecodeException}.
*/
+ @java.lang.Deprecated
public static class IncompleteException extends IOException {};
/**
@@ -453,24 +457,102 @@
};
- /**
- * An Exception was thrown reading the {@link Source}.
+ /** @removed
+ * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
*/
+ @java.lang.Deprecated
public static final int ERROR_SOURCE_EXCEPTION = 1;
- /**
- * The encoded data was incomplete.
+ /** @removed
+ * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
*/
+ @java.lang.Deprecated
public static final int ERROR_SOURCE_INCOMPLETE = 2;
- /**
- * The encoded data contained an error.
+ /** @removed
+ * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
*/
+ @java.lang.Deprecated
public static final int ERROR_SOURCE_ERROR = 3;
- @Retention(SOURCE)
- @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
- public @interface Error {};
+ /**
+ * Information about an interrupted decode.
+ */
+ public static final class DecodeException extends IOException {
+ /**
+ * An Exception was thrown reading the {@link Source}.
+ */
+ public static final int SOURCE_EXCEPTION = 1;
+
+ /**
+ * The encoded data was incomplete.
+ */
+ public static final int SOURCE_INCOMPLETE = 2;
+
+ /**
+ * The encoded data contained an error.
+ */
+ public static final int SOURCE_MALFORMED_DATA = 3;
+
+ /** @hide **/
+ @Retention(SOURCE)
+ @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA },
+ prefix = {"SOURCE_"})
+ public @interface Error {};
+
+ @Error final int mError;
+ @NonNull final Source mSource;
+
+ DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
+ super(errorMessage(error, cause), cause);
+ mError = error;
+ mSource = source;
+ }
+
+ /**
+ * Private method called by JNI.
+ */
+ @SuppressWarnings("unused")
+ DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
+ @NonNull Source source) {
+ super(msg + errorMessage(error, cause), cause);
+ mError = error;
+ mSource = source;
+ }
+
+ /**
+ * Retrieve the reason that decoding was interrupted.
+ *
+ * <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
+ * {@link java.lang.Throwable} can be retrieved with
+ * {@link java.lang.Throwable#getCause}.</p>
+ */
+ @Error
+ public int getError() {
+ return mError;
+ }
+
+ /**
+ * Retrieve the {@link Source} that was interrupted.
+ */
+ @NonNull
+ public Source getSource() {
+ return mSource;
+ }
+
+ private static String errorMessage(@Error int error, @Nullable Throwable cause) {
+ switch (error) {
+ case SOURCE_EXCEPTION:
+ return "Exception in input: " + cause;
+ case SOURCE_INCOMPLETE:
+ return "Input was incomplete.";
+ case SOURCE_MALFORMED_DATA:
+ return "Input contained an error.";
+ default:
+ return "";
+ }
+ }
+ }
/**
* Optional listener supplied to the ImageDecoder.
@@ -486,13 +568,12 @@
* optionally finish the rest of the decode/creation process to create
* a partial {@link Drawable}/{@link Bitmap}.
*
- * @param error indicating what interrupted the decode.
- * @param source that had the error.
+ * @param e containing information about the decode interruption.
* @return True to create and return a {@link Drawable}/{@link Bitmap}
* with partial data. False (which is the default) to abort the
- * decode and throw {@link java.io.IOException}.
+ * decode and throw {@code e}.
*/
- public boolean onPartialImage(@Error int error, @NonNull Source source);
+ boolean onPartialImage(@NonNull DecodeException e);
};
// Fields
@@ -667,6 +748,7 @@
* Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
* @hide
*/
+ @TestApi
public static Source createSource(Resources res, InputStream is, int density) {
return new InputStreamSource(res, is, density);
}
@@ -1087,14 +1169,8 @@
@NonNull
private Bitmap decodeBitmapInternal() throws IOException {
checkState();
- // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
- // exists
- ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
- // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
- // exists.
- ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
- return nDecodeBitmap(mNativePtr, partialImagePtr,
- postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
+ return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
+ mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
mConserveMemory, mDecodeAsAlphaMask);
}
@@ -1310,23 +1386,28 @@
* Private method called by JNI.
*/
@SuppressWarnings("unused")
- private boolean onPartialImage(@Error int error) {
- return mOnPartialImageListener.onPartialImage(error, mSource);
+ private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
+ throws DecodeException {
+ DecodeException exception = new DecodeException(error, cause, mSource);
+ if (mOnPartialImageListener == null
+ || !mOnPartialImageListener.onPartialImage(exception)) {
+ throw exception;
+ }
}
- private static native ImageDecoder nCreate(long asset) throws IOException;
- private static native ImageDecoder nCreate(ByteBuffer buffer,
- int position,
- int limit) throws IOException;
- private static native ImageDecoder nCreate(byte[] data, int offset,
- int length) throws IOException;
- private static native ImageDecoder nCreate(InputStream is, byte[] storage);
+ private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
+ private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
+ int limit, Source src) throws IOException;
+ private static native ImageDecoder nCreate(byte[] data, int offset, int length,
+ Source src) throws IOException;
+ private static native ImageDecoder nCreate(InputStream is, byte[] storage,
+ Source src) throws IOException;
// The fd must be seekable.
- private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
+ private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
@NonNull
private static native Bitmap nDecodeBitmap(long nativePtr,
- @Nullable ImageDecoder partialImageListener,
- @Nullable ImageDecoder postProcessor,
+ @NonNull ImageDecoder decoder,
+ boolean doPostProcess,
int width, int height,
@Nullable Rect cropRect, boolean mutable,
int allocator, boolean requireUnpremul,
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 9ac94d8..d01ff6f 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -209,6 +209,8 @@
public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
mPicture = pict;
+ // Disable bitmap density scaling. This matches DisplayListCanvas.
+ mDensity = 0;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index a47ecf5..457e4aa 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -261,6 +261,12 @@
if (repeatCount != REPEAT_UNDEFINED) {
this.setRepeatCount(repeatCount);
}
+
+ boolean autoStart = a.getBoolean(
+ R.styleable.AnimatedImageDrawable_autoStart, false);
+ if (autoStart && mState.mNativePtr != 0) {
+ this.start();
+ }
}
/**
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d04bb2e..f341cf9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -276,6 +276,8 @@
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
const Typeface* typeface);
+ static int GetApiLevel() { return sApiLevel; }
+
protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9db39d9..0cd1c15 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -342,7 +342,8 @@
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
- if (!opaque) {
+ // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
+ if (!opaque || wideColorGamut) {
canvas->clear(SK_ColorTRANSPARENT);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 820789d..f4d8051 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -355,7 +355,14 @@
// Already drew for this vsync pulse, UI draw request missed
// the deadline for RT animations
info.out.canDrawThisFrame = false;
- } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
+ }
+ /* This logic exists to try and recover from a display latch miss, which essentially
+ * results in the bufferqueue being double-buffered instead of triple-buffered.
+ * SurfaceFlinger itself now tries to handle & recover from this situation, so this
+ * logic should no longer be necessary. As it's occasionally triggering when
+ * undesired disable it.
+ * TODO: Remove this entirely if the results are solid.
+ else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
(latestVsync - mLastDropVsync) < 500_ms) {
// It's been several frame intervals, assume the buffer queue is fine
// or the last drop was too recent
@@ -367,6 +374,7 @@
mLastDropVsync = mRenderThread.timeLord().latestVsync();
}
}
+ */
} else {
info.out.canDrawThisFrame = true;
}
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7ac0d96..4857a87 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -34,7 +34,7 @@
LightBlue_300 = 0xFF4FC3F7,
LightBlue_500 = 0xFF03A9F4,
Cyan_500 = 0xFF00BCD4,
- Teal_500 = 0xFF008577,
+ Teal_500 = 0xFF009688,
Teal_700 = 0xFF00796B,
Green_500 = 0xFF4CAF50,
Green_700 = 0xFF388E3C,
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 412cc291..2152e1e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -83,6 +83,20 @@
*/
public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
+ /**
+ * GNSS measurement tracking loop state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_CODE_LOCK, STATE_BIT_SYNC, STATE_SUBFRAME_SYNC,
+ STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
+ STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
+ STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
+ STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
/** This GNSS measurement's tracking state is invalid or unknown. */
public static final int STATE_UNKNOWN = 0;
/** This GNSS measurement's tracking state has code lock. */
@@ -133,29 +147,68 @@
private static final int STATE_ALL = 0x3fff; // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
/**
- * The state of the 'Accumulated Delta Range' is invalid or unknown.
+ * GNSS measurement accumulated delta range state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
+ ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
+ ADR_STATE_HALF_CYCLE_REPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AdrState {}
+
+ /**
+ * The state of the value {@link #getAccumulatedDeltaRangeMeters()} is invalid or unknown.
*/
public static final int ADR_STATE_UNKNOWN = 0;
/**
- * The state of the 'Accumulated Delta Range' is valid.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} is valid.
*/
public static final int ADR_STATE_VALID = (1<<0);
/**
- * The state of the 'Accumulated Delta Range' has detected a reset.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has detected a reset.
*/
public static final int ADR_STATE_RESET = (1<<1);
/**
- * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has a cycle slip detected.
*/
public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
/**
- * All the 'Accumulated Delta Range' flags.
+ * Reports whether the value {@link #getAccumulatedDeltaRangeMeters()} has resolved the half
+ * cycle ambiguity.
+ *
+ * <p> When this bit is set, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier full cycles.
+ *
+ * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier half cycles.
*/
- private static final int ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+ public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
+
+ /**
+ * Reports whether the flag {@link #ADR_STATE_HALF_CYCLE_RESOLVED} has been reported by the
+ * GNSS hardware.
+ *
+ * <p> When this bit is set, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * can be low (centimeter level) whether or not the half cycle ambiguity is resolved.
+ *
+ * <p> When this bit is unset, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * is larger, to cover the potential error due to half cycle ambiguity being unresolved.
+ */
+ public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
+ /**
+ * All the 'Accumulated Delta Range' flags.
+ * @hide
+ */
+ @TestApi
+ public static final int ADR_STATE_ALL =
+ ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP |
+ ADR_STATE_HALF_CYCLE_RESOLVED | ADR_STATE_HALF_CYCLE_REPORTED;
// End enumerations in sync with gps.h
@@ -278,6 +331,7 @@
*
* <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
*/
+ @State
public int getState() {
return mState;
}
@@ -287,7 +341,7 @@
* @hide
*/
@TestApi
- public void setState(int value) {
+ public void setState(@State int value) {
mState = value;
}
@@ -557,6 +611,7 @@
* <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
* cycle slip (indicating 'loss of lock').
*/
+ @AdrState
public int getAccumulatedDeltaRangeState() {
return mAccumulatedDeltaRangeState;
}
@@ -566,7 +621,7 @@
* @hide
*/
@TestApi
- public void setAccumulatedDeltaRangeState(int value) {
+ public void setAccumulatedDeltaRangeState(@AdrState int value) {
mAccumulatedDeltaRangeState = value;
}
@@ -589,7 +644,15 @@
if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
builder.append("CycleSlip|");
}
- int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_RESOLVED) ==
+ ADR_STATE_HALF_CYCLE_RESOLVED) {
+ builder.append("HalfCycleResolved|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_REPORTED)
+ == ADR_STATE_HALF_CYCLE_REPORTED) {
+ builder.append("HalfCycleReported|");
+ }
+ int remainingStates = mAccumulatedDeltaRangeState & ~ADR_STATE_ALL;
if (remainingStates > 0) {
builder.append("Other(");
builder.append(Integer.toBinaryString(remainingStates));
@@ -674,7 +737,7 @@
* L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
* common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
*
- * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+ * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
* measurement objects will be reported for this same satellite, in one of the measurement
* objects, all the values related to L1 will be filled, and in the other all of the values
* related to L5 will be filled.
@@ -709,7 +772,10 @@
/**
* Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierCycles() {
return isFlagSet(HAS_CARRIER_CYCLES);
}
@@ -720,16 +786,24 @@
* <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
*
* <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
*/
+ @Deprecated
public long getCarrierCycles() {
return mCarrierCycles;
}
/**
* Sets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierCycles(long value) {
setFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = value;
@@ -737,9 +811,13 @@
/**
* Resets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierCycles() {
resetFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = Long.MIN_VALUE;
@@ -747,7 +825,10 @@
/**
* Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierPhase() {
return isFlagSet(HAS_CARRIER_PHASE);
}
@@ -764,16 +845,24 @@
* <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
*
* <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
*/
+ @Deprecated
public double getCarrierPhase() {
return mCarrierPhase;
}
/**
* Sets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierPhase(double value) {
setFlag(HAS_CARRIER_PHASE);
mCarrierPhase = value;
@@ -781,9 +870,14 @@
/**
* Resets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierPhase() {
resetFlag(HAS_CARRIER_PHASE);
mCarrierPhase = Double.NaN;
@@ -792,7 +886,10 @@
/**
* Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
* otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
*/
+ @Deprecated
public boolean hasCarrierPhaseUncertainty() {
return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
}
@@ -803,16 +900,24 @@
* <p>The uncertainty is represented as an absolute (single sided) value.
*
* <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeUncertaintyMeters()} instead.
*/
+ @Deprecated
public double getCarrierPhaseUncertainty() {
return mCarrierPhaseUncertainty;
}
/**
* Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void setCarrierPhaseUncertainty(double value) {
setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = value;
@@ -820,9 +925,14 @@
/**
* Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
* @hide
*/
@TestApi
+ @Deprecated
public void resetCarrierPhaseUncertainty() {
resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = Double.NaN;
@@ -859,7 +969,7 @@
case MULTIPATH_INDICATOR_NOT_DETECTED:
return "NotDetected";
default:
- return "<Invalid:" + mMultipathIndicator + ">";
+ return "<Invalid: " + mMultipathIndicator + ">";
}
}
@@ -916,11 +1026,12 @@
* number. Hence in cases of strong jamming, in the band of this signal, this value will go more
* negative.
*
- * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
* components) may also affect the typical output of of this value on any given hardware design
* in an open sky test - the important aspect of this output is that changes in this value are
* indicative of changes on input signal power in the frequency band for this measurement.
- * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+ *
+ * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
*/
public double getAutomaticGainControlLevelDb() {
return mAutomaticGainControlLevelInDb;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1536bb6..e408a11 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1352,6 +1352,7 @@
//====================================================================
// Offload query
/**
+ * @hide
* Returns whether offloaded playback of an audio format is supported on the device.
* Offloaded playback is where the decoding of an audio stream is not competing with other
* software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 0a1de33..98c2d7f 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -42,6 +42,8 @@
public abstract void setRingerModeInternal(int ringerMode, String caller);
+ public abstract void silenceRingerModeInternal(String caller);
+
public abstract void updateRingerModeAffectedStreamsInternal();
public abstract void setAccessibilityServiceUids(IntArray uids);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3885f90..acab8bb 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -747,7 +747,8 @@
public static final int FOR_SYSTEM = 4;
public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
public static final int FOR_ENCODED_SURROUND = 6;
- private static final int NUM_FORCE_USE = 7;
+ public static final int FOR_VIBRATE_RINGING = 7;
+ private static final int NUM_FORCE_USE = 8;
public static String forceUseUsageToString(int usage) {
switch (usage) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9c48e09..87b5d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -896,6 +896,7 @@
}
/**
+ * @hide
* Sets whether this track will play through the offloaded audio path.
* When set to true, at build time, the audio format will be checked against
* {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
@@ -2979,6 +2980,7 @@
}
/**
+ * @hide
* Abstract class to receive event notification about the stream playback.
* See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
* the callback on the given {@link AudioTrack} instance.
@@ -3012,6 +3014,7 @@
private final Object mStreamEventCbLock = new Object();
/**
+ * @hide
* Sets the callback for the notification of stream events.
* @param executor {@link Executor} to handle the callbacks
* @param eventCallback the callback to receive the stream event notifications
@@ -3031,6 +3034,7 @@
}
/**
+ * @hide
* Unregisters the callback for notification of stream events, previously set
* by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
*/
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 6d58a94..a53fa11 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -40,6 +40,7 @@
import java.util.Map;
/**
+ * @hide
* Structure for data source descriptor.
*
* Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/Media2DataSource.java
index 8ee4a70..08df632 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/Media2DataSource.java
@@ -21,6 +21,7 @@
import java.io.IOException;
/**
+ * @hide
* For supplying media data to the framework. Implement this if your app has
* special requirements for the way media data is obtained.
*
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index f246005..452371a 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -30,6 +30,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Browses media content offered by a {@link MediaLibraryService2}.
*/
public class MediaBrowser2 extends MediaController2 {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index ba9056e..e3fba0c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2359,17 +2359,61 @@
public static final int CRYPTO_MODE_AES_CBC = 2;
/**
- * Metadata describing the structure of a (at least partially) encrypted
- * input sample.
- * A buffer's data is considered to be partitioned into "subSamples",
- * each subSample starts with a (potentially empty) run of plain,
- * unencrypted bytes followed by a (also potentially empty) run of
- * encrypted bytes. If pattern encryption applies, each of the latter runs
- * is encrypted only partly, according to a repeating pattern of "encrypt"
- * and "skip" blocks. numBytesOfClearData can be null to indicate that all
- * data is encrypted. This information encapsulates per-sample metadata as
- * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
- * media file format files".
+ * Metadata describing the structure of an encrypted input sample.
+ * <p>
+ * A buffer's data is considered to be partitioned into "subSamples". Each subSample starts with
+ * a run of plain, unencrypted bytes followed by a run of encrypted bytes. Either of these runs
+ * may be empty. If pattern encryption applies, each of the encrypted runs is encrypted only
+ * partly, according to a repeating pattern of "encrypt" and "skip" blocks.
+ * {@link #numBytesOfClearData} can be null to indicate that all data is encrypted, and
+ * {@link #numBytesOfEncryptedData} can be null to indicate that all data is clear. At least one
+ * of {@link #numBytesOfClearData} and {@link #numBytesOfEncryptedData} must be non-null.
+ * <p>
+ * This information encapsulates per-sample metadata as outlined in ISO/IEC FDIS 23001-7:2016
+ * "Common encryption in ISO base media file format files".
+ * <p>
+ * <h3>ISO-CENC Schemes</h3>
+ * ISO/IEC FDIS 23001-7:2016 defines four possible schemes by which media may be encrypted,
+ * corresponding to each possible combination of an AES mode with the presence or absence of
+ * patterned encryption.
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th> </th>
+ * <th>AES-CTR</th>
+ * <th>AES-CBC</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th>Without Patterns</th>
+ * <td>cenc</td>
+ * <td>cbc1</td>
+ * </tr><tr>
+ * <th>With Patterns</th>
+ * <td>cens</td>
+ * <td>cbcs</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * For {@code CryptoInfo}, the scheme is selected implicitly by the combination of the
+ * {@link #mode} field and the value set with {@link #setPattern}. For the pattern, setting the
+ * pattern to all zeroes (that is, both {@code blocksToEncrypt} and {@code blocksToSkip} are
+ * zero) is interpreted as turning patterns off completely. A scheme that does not use patterns
+ * will be selected, either cenc or cbc1. Setting the pattern to any nonzero value will choose
+ * one of the pattern-supporting schemes, cens or cbcs. The default pattern if
+ * {@link #setPattern} is never called is all zeroes.
+ * <p>
+ * <h4>HLS SAMPLE-AES Audio</h4>
+ * HLS SAMPLE-AES audio is encrypted in a manner compatible with the cbcs scheme, except that it
+ * does not use patterned encryption. However, if {@link #setPattern} is used to set the pattern
+ * to all zeroes, this will be interpreted as selecting the cbc1 scheme. The cbc1 scheme cannot
+ * successfully decrypt HLS SAMPLE-AES audio because of differences in how the IVs are handled.
+ * For this reason, it is recommended that a pattern of {@code 1} encrypted block and {@code 0}
+ * skip blocks be used with HLS SAMPLE-AES audio. This will trigger decryption to use cbcs mode
+ * while still decrypting every block.
*/
public final static class CryptoInfo {
/**
@@ -2377,11 +2421,13 @@
*/
public int numSubSamples;
/**
- * The number of leading unencrypted bytes in each subSample.
+ * The number of leading unencrypted bytes in each subSample. If null, all bytes are treated
+ * as encrypted and {@link #numBytesOfEncryptedData} must be specified.
*/
public int[] numBytesOfClearData;
/**
- * The number of trailing encrypted bytes in each subSample.
+ * The number of trailing encrypted bytes in each subSample. If null, all bytes are treated
+ * as clear and {@link #numBytesOfClearData} must be specified.
*/
public int[] numBytesOfEncryptedData;
/**
@@ -2400,35 +2446,34 @@
public int mode;
/**
- * Metadata describing an encryption pattern for the protected bytes in
- * a subsample. An encryption pattern consists of a repeating sequence
- * of crypto blocks comprised of a number of encrypted blocks followed
- * by a number of unencrypted, or skipped, blocks.
+ * Metadata describing an encryption pattern for the protected bytes in a subsample. An
+ * encryption pattern consists of a repeating sequence of crypto blocks comprised of a
+ * number of encrypted blocks followed by a number of unencrypted, or skipped, blocks.
*/
public final static class Pattern {
/**
- * Number of blocks to be encrypted in the pattern. If zero, pattern
- * encryption is inoperative.
+ * Number of blocks to be encrypted in the pattern. If both this and
+ * {@link #mSkipBlocks} are zero, pattern encryption is inoperative.
*/
private int mEncryptBlocks;
/**
- * Number of blocks to be skipped (left clear) in the pattern. If zero,
- * pattern encryption is inoperative.
+ * Number of blocks to be skipped (left clear) in the pattern. If both this and
+ * {@link #mEncryptBlocks} are zero, pattern encryption is inoperative.
*/
private int mSkipBlocks;
/**
- * Construct a sample encryption pattern given the number of blocks to
- * encrypt and skip in the pattern.
+ * Construct a sample encryption pattern given the number of blocks to encrypt and skip
+ * in the pattern. If both parameters are zero, pattern encryption is inoperative.
*/
public Pattern(int blocksToEncrypt, int blocksToSkip) {
set(blocksToEncrypt, blocksToSkip);
}
/**
- * Set the number of blocks to encrypt and skip in a sample encryption
- * pattern.
+ * Set the number of blocks to encrypt and skip in a sample encryption pattern. If both
+ * parameters are zero, pattern encryption is inoperative.
*/
public void set(int blocksToEncrypt, int blocksToSkip) {
mEncryptBlocks = blocksToEncrypt;
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 14dcced..234a4f4 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -40,6 +40,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Allows an app to interact with an active {@link MediaSession2} or a
* {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
* the session.
@@ -735,8 +736,10 @@
return mProvider.getPlaylistMetadata_impl();
}
+
/**
- * Inserts the media item to the playlist at position index.
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
* <p>
* This will not change the currently playing media item.
* If index is less than or equal to the current index of the playlist,
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 8d62bd4..1967a1c 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -28,6 +28,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* A class with information on a single media item with the metadata information.
* Media item are application dependent so we cannot guarantee that they contain the right values.
* <p>
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index f3684d6..034d17e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -34,6 +34,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Base class for media library services.
* <p>
* Media library services enable applications to browse media content provided by an application
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 2ba66b2..1a15962 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -31,6 +31,7 @@
import java.util.Set;
/**
+ * @hide
* Contains metadata about an item, such as the title, artist, etc.
*/
// New version of MediaMetadata with following changes
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index ece19b9..dcc872c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -43,6 +43,7 @@
/**
+ * @hide
* MediaPlayer2 class can be used to control playback
* of audio/video files and streams. An example on how to use the methods in
* this class can be found in {@link android.widget.VideoView}.
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 48b7a51..1fcf02b 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -26,6 +26,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Base class for all media players that want media session.
*/
public abstract class MediaPlayerBase implements AutoCloseable {
@@ -306,8 +307,9 @@
public static abstract class PlayerEventCallback {
/**
* Called when the player's current data source has changed.
+ *
* @param mpb the player whose data source changed.
- * @param dsd the new current data source.
+ * @param dsd the new current data source. null, if no more data sources available.
*/
public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
@Nullable DataSourceDesc dsd) { }
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
index 1250810..453e24a 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -30,6 +30,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
* to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
* notify methods to signal MediaSession2 that playlist-related state has changed.
@@ -228,10 +229,15 @@
}
/**
- * Adds the media item to the playlist at the index
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
+ * <p>
+ * This will not change the currently playing media item.
+ * If index is less than or equal to the current index of the playlist,
+ * the current index of the playlist will be incremented correspondingly.
*
- * @param index index
- * @param item media item to add
+ * @param index the index you want to add
+ * @param item the media item you want to add
*/
public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
mProvider.addPlaylistItem_impl(index, item);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 472d942..0f4b5da 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -49,6 +49,7 @@
import java.util.concurrent.Executor;
/**
+ * @hide
* Allows a media app to expose its transport controls and playback information in a process to
* other processes including the Android framework and other apps. Common use cases are as follows.
* <ul>
@@ -1541,7 +1542,7 @@
* @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
*/
public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
- // TODO(jaewan): Implement (b/74090741).
+ mProvider.setOnDataSourceMissingHelper_impl(helper);
}
/**
@@ -1550,7 +1551,7 @@
* @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
*/
public void clearOnDataSourceMissingHelper() {
- // TODO(jaewan): Implement (b/74090741)
+ mProvider.clearOnDataSourceMissingHelper_impl();
}
/**
@@ -1646,7 +1647,8 @@
}
/**
- * Adds the media item to the playlist at position index.
+ * Adds the media item to the playlist at position index. Index equals or greater than
+ * the current playlist size will add the item at the end of the playlist.
* <p>
* This will not change the currently playing media item.
* If index is less than or equal to the current index of the play list,
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index b830694..85ac9b2 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
/**
+ * @hide
* Base class for media session services, which is the service version of the {@link MediaSession2}.
* <p>
* It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index 68a5641..f088be3 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -28,6 +28,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
* If it's representing a session service, it may not be ongoing.
* <p>
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 8501924..2d96d096 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -26,6 +26,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session. The provider must call
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 963457b..56664a9 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -52,12 +52,13 @@
void setOnMediaKeyListener(in IOnMediaKeyListener listener);
// MediaSession2
- boolean isTrusted(int uid, String packageName);
+ boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
boolean createSession2(in Bundle sessionToken);
void destroySession2(in Bundle sessionToken);
- List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
+ List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
+ String packageName);
void addSessionTokensListener(in ISessionTokensListener listener, int userId,
String packageName);
- void removeSessionTokensListener(in ISessionTokensListener listener);
+ void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 051321c..b7f49988 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -342,16 +342,17 @@
/**
* Returns whether the api
*
- * @param uid uid of the app
* @param packageName packageName
+ * @param pid pid of the app
+ * @param uid uid of the app
* @hide
*/
- public boolean isTrusted(int uid, @NonNull String packageName) {
+ public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
if (packageName == null) {
return false;
}
try {
- return mService.isTrusted(uid, packageName);
+ return mService.isTrusted(packageName, pid, uid);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
}
@@ -390,6 +391,7 @@
}
/**
+ * @hide
* Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
* active sessions regardless of whether they're {@link MediaSession2} or
* {@link MediaSessionService2}.
@@ -403,7 +405,8 @@
public List<SessionToken2> getActiveSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+ /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -412,6 +415,7 @@
}
/**
+ * @hide
* Get {@link List} of {@link SessionToken2} for {@link MediaSessionService2} regardless of their
* activeness. This list represents media apps that support background playback.
* <p>
@@ -424,7 +428,8 @@
public List<SessionToken2> getSessionServiceTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+ /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -433,6 +438,7 @@
}
/**
+ * @hide
* Get all {@link SessionToken2}s. This is the combined list of {@link #getActiveSessionTokens()}
* and {@link #getSessionServiceTokens}.
* <p>
@@ -447,7 +453,8 @@
public List<SessionToken2> getAllSessionTokens() {
try {
List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+ /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
+ mContext.getPackageName());
return toTokenList(mContext, bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -456,6 +463,7 @@
}
/**
+ * @hide
* Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
* <p>
* This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
@@ -508,6 +516,7 @@
}
/**
+ * @hide
* Stop receiving session token updates on the specified listener.
*
* @param listener The listener to remove.
@@ -521,7 +530,7 @@
SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
if (wrapper != null) {
try {
- mService.removeSessionTokensListener(wrapper.mStub);
+ mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error in removeSessionTokensListener.", e);
} finally {
@@ -666,6 +675,7 @@
}
/**
+ * @hide
* Listens for changes to the {@link #getAllSessionTokens()}. This can be added
* using {@link #addOnActiveSessionsChangedListener}.
*/
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index e5ea386..0faed9d 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -21,7 +21,6 @@
import android.media.MediaItem2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.PlayerEventCallback;
import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
@@ -29,6 +28,7 @@
import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
import android.media.MediaSession2.SessionCallback;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
@@ -68,6 +68,8 @@
int getPlayerState_impl();
long getPosition_impl();
long getBufferedPosition_impl();
+ void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
+ void clearOnDataSourceMissingHelper_impl();
interface CommandProvider {
int getCommandCode_impl();
diff --git a/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml b/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
index b8a0689..8196650 100644
--- a/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
+++ b/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
@@ -18,7 +18,7 @@
android:height="36dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0"
- android:tint="@*android:color/accent_device_default_light">
+ android:tint="@color/pdf_printer_color">
<path
android:pathData="M40,4L16,4c-2.21,0 -4,1.79 -4,4v24c0,2.21 1.79,4 4,4h24c2.21,0 4,-1.79 4,-4L44,8c0,-2.21 -1.79,-4 -4,-4zM23,19c0,1.66 -1.34,3 -3,3h-2v4h-3L15,14h5c1.66,0 3,1.34 3,3v2zM33,23c0,1.66 -1.34,3 -3,3h-5L25,14h5c1.66,0 3,1.34 3,3v6zM41,17h-3v2h3v3h-3v4h-3L35,14h6v3zM18,19h2v-2h-2v2zM8,12L4,12v28c0,2.21 1.79,4 4,4h28v-4L8,40L8,12zM28,23h2v-6h-2v6z"
android:fillColor="@android:color/black"/>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 68bc6f2..a15fff5 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -23,4 +23,6 @@
<color name="unselected_page_background_color">#C0C0C0</color>
<color name="material_grey_500">#ffa3a3a3</color>
+
+ <color name="pdf_printer_color">#009688</color>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 589608a..1a9ce95 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -150,6 +150,13 @@
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
+ <!-- Connected devices settings. Message when Bluetooth is connected and active, showing remote device status and battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_active_battery_level">Active, <xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_active_no_battery_level">Active</string>
+
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the headset or handsfree profile. -->
@@ -1093,4 +1100,7 @@
<string name="zen_mode_duration_settings_title">Duration</string>
<!-- Do not disturb: Duration option to always prompt for the duration of dnd -->
<string name="zen_mode_duration_always_prompt_title">Ask every time</string>
+
+ <!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
+ <string name="time_unit_just_now">Just now</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 3a0ae9f..093b1bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
@@ -59,6 +60,8 @@
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -135,6 +138,38 @@
final BackgroundHandler mBackgroundHandler;
final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
+ /** Requests that the home app is loaded. */
+ public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
+
+ /** Requests that icons are loaded. */
+ public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
+
+ /** Requests that sizes are loaded. */
+ public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
+
+ /** Requests that launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
+
+ /** Requests that leanback launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
+
+ /**
+ * Flags to configure the session to request various types of info.
+ */
+ @IntDef(prefix = { "FLAG_SESSION_" }, value = {
+ FLAG_SESSION_REQUEST_HOME_APP,
+ FLAG_SESSION_REQUEST_ICONS,
+ FLAG_SESSION_REQUEST_SIZES,
+ FLAG_SESSION_REQUEST_LAUNCHER,
+ FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionFlags {}
+
+ public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+ FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
+ FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+
private ApplicationsState(Application app) {
mContext = app;
mPm = mContext.getPackageManager();
@@ -265,7 +300,8 @@
}
}
- private void clearEntries() {
+ @VisibleForTesting
+ void clearEntries() {
for (int i = 0; i < mEntriesMap.size(); i++) {
mEntriesMap.valueAt(i).clear();
}
@@ -346,7 +382,7 @@
if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
- if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
mBackgroundHandler.post(
() -> {
try {
@@ -595,6 +631,7 @@
}
public class Session implements LifecycleObserver {
+
final Callbacks mCallbacks;
boolean mResumed;
@@ -609,6 +646,7 @@
boolean mRebuildForeground;
private final boolean mHasLifecycle;
+ @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
@@ -620,6 +658,14 @@
}
}
+ public @SessionFlags int getSessionFlags() {
+ return mFlags;
+ }
+
+ public void setSessionFlags(@SessionFlags int flags) {
+ mFlags = flags;
+ }
+
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
@@ -833,10 +879,11 @@
private class BackgroundHandler extends Handler {
static final int MSG_REBUILD_LIST = 1;
static final int MSG_LOAD_ENTRIES = 2;
- static final int MSG_LOAD_ICONS = 3;
- static final int MSG_LOAD_SIZES = 4;
- static final int MSG_LOAD_LAUNCHER = 5;
- static final int MSG_LOAD_HOME_APP = 6;
+ static final int MSG_LOAD_HOME_APP = 3;
+ static final int MSG_LOAD_LAUNCHER = 4;
+ static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
+ static final int MSG_LOAD_ICONS = 6;
+ static final int MSG_LOAD_SIZES = 7;
boolean mRunning;
@@ -860,6 +907,8 @@
}
}
+ int flags = getCombinedSessionFlags(mSessions);
+
switch (msg.what) {
case MSG_REBUILD_LIST: {
} break;
@@ -889,8 +938,8 @@
// happens because of the way we generate the list in
// doResumeIfNeededLocked.
AppEntry entry = mEntriesMap.get(0).get(info.packageName);
- if (entry != null &&
- (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ if (entry != null && !hasFlag(entry.info.flags,
+ ApplicationInfo.FLAG_INSTALLED)) {
mEntriesMap.get(0).remove(info.packageName);
mAppEntries.remove(entry);
}
@@ -909,166 +958,206 @@
}
} break;
case MSG_LOAD_HOME_APP: {
- final List<ResolveInfo> homeActivities = new ArrayList<>();
- mPm.getHomeActivities(homeActivities);
- synchronized (mEntriesMap) {
- final int entryCount = mEntriesMap.size();
- for (int i = 0; i < entryCount; i++) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
- final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- for (ResolveInfo activity : homeActivities) {
- String packageName = activity.activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.isHomeApp = true;
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+ mPm.getHomeActivities(homeActivities);
+ synchronized (mEntriesMap) {
+ final int entryCount = mEntriesMap.size();
+ for (int i = 0; i < entryCount; i++) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+ final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
+ i);
+ for (ResolveInfo activity : homeActivities) {
+ String packageName = activity.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.isHomeApp = true;
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
}
sendEmptyMessage(MSG_LOAD_LAUNCHER);
- }
- break;
- case MSG_LOAD_LAUNCHER: {
- Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER);
- for (int i = 0; i < mEntriesMap.size(); i++) {
- int userId = mEntriesMap.keyAt(i);
- // If we do not specify MATCH_DIRECT_BOOT_AWARE or
- // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
- // according to the user's lock state. When the user is locked, components
- // with ComponentInfo#directBootAware == false will be filtered. We should
- // explicitly include both direct boot aware and unaware components here.
- List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
- launchIntent,
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId
- );
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
- HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- final int N = intents.size();
- for (int j = 0; j < N; j++) {
- String packageName = intents.get(j).activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.hasLauncherEntry = true;
- } else {
- Log.w(TAG, "Cannot find pkg: " + packageName
- + " on user " + userId);
+ } break;
+ case MSG_LOAD_LAUNCHER:
+ case MSG_LOAD_LEANBACK_LAUNCHER: {
+ if ((msg.what == MSG_LOAD_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
+ || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
+
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+ launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
+ ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ int userId = mEntriesMap.keyAt(i);
+ // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+ // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+ // according to the user's lock state. When the user is locked,
+ // components
+ // with ComponentInfo#directBootAware == false will be filtered. We should
+ // explicitly include both direct boot aware and unaware components here.
+ List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+ launchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId
+ );
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+ HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+ final int N = intents.size();
+ for (int j = 0; j < N; j++) {
+ ResolveInfo resolveInfo = intents.get(j);
+ String packageName = resolveInfo.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.hasLauncherEntry = true;
+ entry.launcherEntryEnabled |=
+ resolveInfo.activityInfo.enabled;
+ } else {
+ Log.w(TAG, "Cannot find pkg: " + packageName
+ + " on user " + userId);
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+ }
+
+ if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
}
}
-
- if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+ if (msg.what == MSG_LOAD_LAUNCHER) {
+ sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
+ } else {
+ sendEmptyMessage(MSG_LOAD_ICONS);
}
- sendEmptyMessage(MSG_LOAD_ICONS);
} break;
case MSG_LOAD_ICONS: {
- int numDone = 0;
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
- for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
- AppEntry entry = mAppEntries.get(i);
- if (entry.icon == null || !entry.mounted) {
- synchronized (entry) {
- if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+ for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.icon == null || !entry.mounted) {
+ synchronized (entry) {
+ if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ numDone++;
+ }
+ }
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+ }
+ if (numDone > 0) {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+ }
+ }
+ if (numDone >= 2) {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ break;
+ }
+ }
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ } break;
+ case MSG_LOAD_SIZES: {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+ if (mCurComputingSizePkg != null) {
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: currently computing");
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ for (int i = 0; i < mAppEntries.size(); i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
+ && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
+ if (entry.sizeLoadStart == 0 ||
+ (entry.sizeLoadStart < (now - 20 * 1000))) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
mMainHandler.sendMessage(m);
}
- numDone++;
+ entry.sizeLoadStart = now;
+ mCurComputingSizeUuid = entry.info.storageUuid;
+ mCurComputingSizePkg = entry.info.packageName;
+ mCurComputingSizeUserId = UserHandle.getUserId(
+ entry.info.uid);
+
+ mBackgroundHandler.post(() -> {
+ try {
+ final StorageStats stats =
+ mStats.queryStatsForPackage(
+ mCurComputingSizeUuid,
+ mCurComputingSizePkg,
+ UserHandle.of(
+ mCurComputingSizeUserId));
+ final PackageStats legacy = new PackageStats(
+ mCurComputingSizePkg,
+ mCurComputingSizeUserId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacy,
+ true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ });
}
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: now computing");
+ return;
}
}
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
- }
- if (numDone > 0) {
- if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
- }
- }
- if (numDone >= 2) {
- sendEmptyMessage(MSG_LOAD_ICONS);
- } else {
- sendEmptyMessage(MSG_LOAD_SIZES);
- }
- } break;
- case MSG_LOAD_SIZES: {
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
- if (mCurComputingSizePkg != null) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mAppEntries.size(); i++) {
- AppEntry entry = mAppEntries.get(i);
- if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
- && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
- if (entry.sizeLoadStart == 0 ||
- (entry.sizeLoadStart < (now-20*1000))) {
- if (!mRunning) {
- mRunning = true;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
- mMainHandler.sendMessage(m);
- }
- entry.sizeLoadStart = now;
- mCurComputingSizeUuid = entry.info.storageUuid;
- mCurComputingSizePkg = entry.info.packageName;
- mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-
- mBackgroundHandler.post(() -> {
- try {
- final StorageStats stats = mStats.queryStatsForPackage(
- mCurComputingSizeUuid, mCurComputingSizePkg,
- UserHandle.of(mCurComputingSizeUserId));
- final PackageStats legacy = new PackageStats(
- mCurComputingSizePkg, mCurComputingSizeUserId);
- legacy.codeSize = stats.getCodeBytes();
- legacy.dataSize = stats.getDataBytes();
- legacy.cacheSize = stats.getCacheBytes();
- try {
- mStatsObserver.onGetStatsCompleted(legacy, true);
- } catch (RemoteException ignored) {
- }
- } catch (NameNotFoundException | IOException e) {
- Log.w(TAG, "Failed to query stats: " + e);
- try {
- mStatsObserver.onGetStatsCompleted(null, false);
- } catch (RemoteException ignored) {
- }
- }
-
- });
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
- return;
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+ mRunning = false;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+ mMainHandler.sendMessage(m);
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
- if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
- mRunning = false;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
- mMainHandler.sendMessage(m);
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
} break;
}
}
+ private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+ synchronized (mEntriesMap) {
+ int flags = 0;
+ for (Session session : sessions) {
+ flags |= session.mFlags;
+ }
+ return flags;
+ }
+ }
+
final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
if (!succeeded) {
@@ -1257,6 +1346,11 @@
public boolean hasLauncherEntry;
/**
+ * Whether the component that has a launcher intent filter is enabled.
+ */
+ public boolean launcherEntryEnabled;
+
+ /**
* Whether or not it's a Home app.
*/
public boolean isHomeApp;
@@ -1283,7 +1377,7 @@
// A location where extra info can be placed to be used by custom filters.
public Object extraInfo;
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public AppEntry(Context context, ApplicationInfo info, long id) {
apkFile = new File(info.sourceDir);
this.id = id;
@@ -1336,6 +1430,10 @@
}
}
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
+
/**
* Compare by label, then package name, then uid.
*/
@@ -1449,13 +1547,13 @@
public boolean filterApp(AppEntry entry) {
if (AppUtils.isInstant(entry.info)) {
return false;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
} else if (entry.hasLauncherEntry) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
return true;
}
return false;
@@ -1486,9 +1584,9 @@
@Override
public boolean filterApp(AppEntry entry) {
- if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
}
return false;
@@ -1547,7 +1645,7 @@
@Override
public boolean filterApp(AppEntry entry) {
return !AppUtils.isInstant(entry.info)
- && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
}
};
@@ -1589,7 +1687,7 @@
// TODO: Update for the new game category.
boolean isGame;
synchronized (info.info) {
- isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+ isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
|| info.info.category == ApplicationInfo.CATEGORY_GAME;
}
return isGame;
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 3c02f6a..2d3dfe4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -59,7 +59,8 @@
@Override
public boolean isAvailable() {
- return mContext.getSystemService(UserManager.class).isAdminUser();
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ return um != null && (um.isAdminUser() || um.isDemoUser());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
index 85bf4e8..d21da4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -45,8 +45,7 @@
Build.TYPE.equals("eng") ? 1 : 0) != 0;
final boolean hasRestriction = um.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES);
- final boolean isAdmin = um.isAdminUser();
-
- return isAdmin && !hasRestriction && settingEnabled;
+ final boolean isAdminOrDemo = um.isAdminUser() || um.isDemoUser();
+ return isAdminOrDemo && !hasRestriction && settingEnabled;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 68be2b4..81a2d43 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -27,6 +27,9 @@
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TtsSpan;
+
+import com.android.settingslib.R;
+
import java.util.ArrayList;
import java.util.Locale;
@@ -121,8 +124,7 @@
final RelativeUnit unit;
final int value;
if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
- unit = RelativeUnit.SECONDS;
- value = seconds;
+ return context.getResources().getString(R.string.time_unit_just_now);
} else if (seconds < 2 * SECONDS_PER_HOUR) {
unit = RelativeUnit.MINUTES;
value = (seconds + SECONDS_PER_MINUTE / 2)
@@ -141,7 +143,7 @@
final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
ULocale.forLocale(locale),
null /* default NumberFormat */,
- RelativeDateTimeFormatter.Style.SHORT,
+ RelativeDateTimeFormatter.Style.LONG,
android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
new file mode 100644
index 0000000..2dbabe0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.UserIdInt;
+import android.app.ApplicationPackageManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+ ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
+ ApplicationsStateRoboTest.ShadowPackageManager.class})
+public class ApplicationsStateRoboTest {
+
+ private final static String HOME_PACKAGE_NAME = "com.android.home";
+ private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
+
+ /** Class under test */
+ private ApplicationsState mApplicationsState;
+
+ @Mock
+ private Callbacks mCallbacks;
+ @Captor
+ private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
+ @Mock
+ private StorageStatsManager mStorageStatsManager;
+
+ @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+ public static class ShadowIconDrawableFactory {
+
+ @Implementation
+ public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+ return new ColorDrawable(0);
+ }
+ }
+
+ @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+ public static class ShadowPackageManager extends
+ org.robolectric.shadows.ShadowApplicationPackageManager {
+
+ @Implementation
+ public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ outActivities.add(resolveInfo);
+ return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
+ }
+
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
+ @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+ List<ResolveInfo> resolveInfos = new ArrayList<>();
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ resolveInfo.filter = new IntentFilter();
+ resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
+ resolveInfos.add(resolveInfo);
+ return resolveInfos;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Robolectric does not know about the StorageStatsManager as a system service.
+ // Registering a mock of this service as a replacement.
+ ShadowContextImpl shadowContext = Shadow.extract(
+ RuntimeEnvironment.application.getBaseContext());
+ shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+ StorageStats storageStats = new StorageStats();
+ storageStats.codeBytes = 10;
+ storageStats.dataBytes = 20;
+ storageStats.cacheBytes = 30;
+ when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
+ anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+
+ mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+ mApplicationsState.clearEntries();
+ }
+
+ private ApplicationInfo createApplicationInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.sourceDir = "foo";
+ appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ appInfo.storageUuid = UUID.randomUUID();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+
+ private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+ AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
+ appEntry.label = "label";
+ appEntry.mounted = true;
+ return appEntry;
+ }
+
+ private void addApp(String packageName, int id) {
+ ApplicationInfo appInfo = createApplicationInfo(packageName);
+ AppEntry appEntry = createAppEntry(appInfo, id);
+ mApplicationsState.mAppEntries.add(appEntry);
+ mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry);
+ }
+
+ private void processAllMessages() {
+ Handler mainHandler = mApplicationsState.mMainHandler;
+ Handler bkgHandler = mApplicationsState.mBackgroundHandler;
+ ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
+ ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
+ shadowBkgLooper.idle();
+ shadowMainLooper.idle();
+ }
+
+ private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
+ for (AppEntry appEntry : appEntries) {
+ if (appEntry.id == id) {
+ return appEntry;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testDefaultSessionLoadsAll() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ addApp(LAUNCHABLE_PACKAGE_NAME,2);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(2);
+
+ for (AppEntry appEntry : appEntries) {
+ assertThat(appEntry.size).isGreaterThan(0L);
+ assertThat(appEntry.icon).isNotNull();
+ }
+
+ AppEntry homeEntry = findAppEntry(appEntries, 1);
+ assertThat(homeEntry.isHomeApp).isTrue();
+ assertThat(homeEntry.hasLauncherEntry).isFalse();
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 2);
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsIconsOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNotNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsSizesOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isGreaterThan(0L);
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsHomeOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsLeanbackOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isFalse();
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a15f5fc..ccaf3fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -73,11 +73,22 @@
}
@Test
- public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
+ public void isEnabled_settingsOn_noRestriction_notAdmin_notDemo_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
ShadowUserManager.getShadow().setIsAdminUser(false);
+ ShadowUserManager.getShadow().setIsDemoUser(false);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
+
+ @Test
+ public void isEnabled_settingsOn_noRestriction_notAdmin_isDemo_shouldReturnTrue() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ ShadowUserManager.getShadow().setIsAdminUser(false);
+ ShadowUserManager.getShadow().setIsDemoUser(true);
+
+ assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a3e1bc8..bbd3a53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
package com.android.settingslib.testutils.shadow;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.UserManager;
import org.robolectric.RuntimeEnvironment;
@@ -25,6 +26,9 @@
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
+import java.util.List;
+
@Implements(value = UserManager.class, inheritImplementationMethods = true)
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
@@ -49,6 +53,20 @@
return (UserManager) context.getSystemService(Context.USER_SERVICE);
}
+ @Implementation
+ public int[] getProfileIdsWithDisabled(int userId) {
+ return new int[] { 0 };
+ }
+
+ @Implementation
+ public List<UserInfo> getProfiles() {
+ UserInfo userInfo = new UserInfo();
+ userInfo.id = 0;
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(userInfo);
+ return userInfos;
+ }
+
public static ShadowUserManager getShadow() {
return (ShadowUserManager) Shadow.extract(
RuntimeEnvironment.application.getSystemService(UserManager.class));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index 47dd022..532c755 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -114,7 +114,7 @@
@Test
public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
- final String expectedTime = "40 sec. ago";
+ final String expectedTime = "Just now";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -123,7 +123,7 @@
@Test
public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
- final String expectedTime = "1 min. ago";
+ final String expectedTime = "1 minute ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
expectedTime);
@@ -132,7 +132,7 @@
@Test
public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
- final String expectedTime = "119 sec. ago";
+ final String expectedTime = "Just now";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -141,7 +141,7 @@
@Test
public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
- final String expectedTime = "2 min. ago";
+ final String expectedTime = "2 minutes ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
expectedTime);
@@ -150,7 +150,7 @@
@Test
public void testFormatRelativeTime_TwoMinutes_withSeconds() {
final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
- final String expectedTime = "2 min. ago";
+ final String expectedTime = "2 minutes ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -159,7 +159,7 @@
@Test
public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
- final String expectedTime = "119 min. ago";
+ final String expectedTime = "119 minutes ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -168,7 +168,7 @@
@Test
public void testFormatRelativeTime_TwoHours_withSeconds() {
final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
- final String expectedTime = "2 hr. ago";
+ final String expectedTime = "2 hours ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -177,7 +177,7 @@
@Test
public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
- final String expectedTime = "47 hr. ago";
+ final String expectedTime = "47 hours ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -195,7 +195,7 @@
@Test
public void testFormatRelativeTime_FormatZero_WithSeconds() {
final double testMillis = 0;
- final String expectedTime = "0 sec. ago";
+ final String expectedTime = "Just now";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
expectedTime);
@@ -204,7 +204,7 @@
@Test
public void testFormatRelativeTime_FormatZero_NoSeconds() {
final double testMillis = 0;
- final String expectedTime = "0 min. ago";
+ final String expectedTime = "0 minutes ago";
assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
expectedTime);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 797b4f9..2047f58 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -578,6 +578,9 @@
Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
GlobalSettingsProto.BLE_SCAN_LOW_LATENCY_INTERVAL_MS);
dumpSetting(s, p,
+ Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+ GlobalSettingsProto.BLE_SCAN_BACKGROUND_MODE);
+ dumpSetting(s, p,
Settings.Global.WIFI_SAVED_STATE,
GlobalSettingsProto.WIFI_SAVED_STATE);
dumpSetting(s, p,
@@ -1928,6 +1931,9 @@
dumpSetting(s, p,
Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
+ dumpSetting(s, p,
+ Settings.Secure.VOLUME_HUSH_GESTURE,
+ SecureSettingsProto.VOLUME_HUSH_GESTURE);
// Please insert new settings using the same order as in Settings.Secure.
p.end(token);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index b37071b..7fd0698 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2914,7 +2914,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 161;
+ private static final int SETTINGS_VERSION = 162;
private final int mUserId;
@@ -3673,6 +3673,21 @@
currentVersion = 161;
}
+ if (currentVersion == 161) {
+ // Version 161: Add a gesture for silencing phones
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting currentSetting = secureSettings.getSettingLocked(
+ Secure.VOLUME_HUSH_GESTURE);
+ if (currentSetting.isNull()) {
+ secureSettings.insertSettingLocked(
+ Secure.VOLUME_HUSH_GESTURE,
+ Integer.toString(Secure.VOLUME_HUSH_VIBRATE),
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 162;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 2da03d2..cc6e3bf 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,6 +50,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
+ android:paddingEnd="1dp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
diff --git a/packages/SystemUI/res/values-sw600dp/bools.xml b/packages/SystemUI/res/values-sw600dp/bools.xml
new file mode 100644
index 0000000..05e38bd
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+ <bool name="qs_show_user_switcher_for_single_user">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
new file mode 100644
index 0000000..499e24e
--- /dev/null
+++ b/packages/SystemUI/res/values/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+ <bool name="qs_show_user_switcher_for_single_user">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5b038b1..906ca4a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -79,9 +79,6 @@
<!-- The color of the material notification background when dark -->
<color name="notification_material_background_dark_color">#ff333333</color>
- <!-- The color of the material notification background when low priority -->
- <color name="notification_material_background_low_priority_color">#fff5f5f5</color>
-
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">#FF616161</color>
@@ -91,9 +88,6 @@
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#28000000</color>
- <!-- The color of the ripples on the low priority notifications -->
- <color name="notification_ripple_color_low_priority">#30000000</color>
-
<!-- The color of the ripples on the tinted notifications -->
<color name="notification_ripple_tinted_color">#30ffffff</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 375c31a..a4b7608 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1353,8 +1353,6 @@
<string name="volume_dialog_title">%s volume controls</string>
- <string name="volume_dialog_ringer_guidance_vibrate">Calls and notifications will vibrate</string>
- <string name="volume_dialog_ringer_guidance_silent">Calls and notifications will be muted</string>
<string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring</string>
<string name="output_title">Media output</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ea3a60b..8923952 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -42,16 +42,14 @@
};
- private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
- private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
+ private final ArrayList<TileRecord> mTiles = new ArrayList<>();
+ private final ArrayList<TilePage> mPages = new ArrayList<>();
private PageIndicator mPageIndicator;
private int mNumPages;
private PageListener mPageListener;
- private int mPosition;
- private boolean mOffPage;
private boolean mListening;
private Scroller mScroller;
@@ -85,16 +83,12 @@
public void setListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
- if (mListening) {
- setPageListening(mPosition, true);
- if (mOffPage) {
- setPageListening(mPosition + 1, true);
- }
- } else {
- // Make sure no pages are listening.
- for (int i = 0; i < mPages.size(); i++) {
- mPages.get(i).setListening(false);
- }
+ updateListening();
+ }
+
+ private void updateListening() {
+ for (TilePage tilePage : mPages) {
+ tilePage.setListening(tilePage.getParent() == null ? false : mListening);
}
}
@@ -137,43 +131,6 @@
super.computeScroll();
}
- /**
- * Sets individual pages to listening or not. If offPage it will set
- * the next page after position to listening as well since we are in between
- * pages.
- */
- private void setCurrentPage(int position, boolean offPage) {
- if (mPosition == position && mOffPage == offPage) return;
- if (mListening) {
- if (mPosition != position) {
- // Clear out the last pages from listening.
- setPageListening(mPosition, false);
- if (mOffPage) {
- setPageListening(mPosition + 1, false);
- }
- // Set the new pages to listening
- setPageListening(position, true);
- if (offPage) {
- setPageListening(position + 1, true);
- }
- } else if (mOffPage != offPage) {
- // Whether we are showing position + 1 has changed.
- setPageListening(mPosition + 1, offPage);
- }
- }
- // Save the current state.
- mPosition = position;
- mOffPage = offPage;
- }
-
- private void setPageListening(int position, boolean listening) {
- if (position >= mPages.size()) return;
- if (isLayoutRtl()) {
- position = mPages.size() - 1 - position;
- }
- mPages.get(position).setListening(listening);
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -362,7 +319,6 @@
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (mPageIndicator == null) return;
- setCurrentPage(position, positionOffset != 0);
mPageIndicator.setLocation(position + positionOffset);
if (mPageListener != null) {
mPageListener.onPageChanged(positionOffsetPixels == 0 &&
@@ -407,11 +363,14 @@
}
private final PagerAdapter mAdapter = new PagerAdapter() {
+ @Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (DEBUG) Log.d(TAG, "Destantiating " + position);
container.removeView((View) object);
+ updateListening();
}
+ @Override
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "Instantiating " + position);
if (isLayoutRtl()) {
@@ -419,6 +378,7 @@
}
ViewGroup view = mPages.get(position);
container.addView(view);
+ updateListening();
return view;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7da109d..bfbfbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -140,7 +140,6 @@
setMargins(mQSFooter);
setMargins(mQSPanel);
setMargins(mHeader);
- setMargins(mQSCustomizer);
}
private void setMargins(View view) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index dbf1745..0fa6597 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.PorterDuff.Mode;
@@ -49,7 +50,6 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
@@ -257,17 +257,31 @@
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
-
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
-
-
- mMultiUserSwitch.setVisibility(mExpanded
- && UserManager.get(mContext).isUserSwitcherEnabled()
- ? View.VISIBLE : View.INVISIBLE);
-
+ mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
}
+ private boolean showUserSwitcher(boolean isDemo) {
+ if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
+ return false;
+ }
+ UserManager userManager = UserManager.get(mContext);
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
+ return false;
+ }
+ int switchableUserCount = 0;
+ for (UserInfo user : userManager.getUsers(true)) {
+ if (user.supportsSwitchToByUser()) {
+ ++switchableUserCount;
+ if (switchableUserCount > 1) {
+ return true;
+ }
+ }
+ }
+ return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
+ }
+
private void updateListeners() {
if (mListening) {
mUserInfoController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 9759b69..eb95866 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -15,42 +15,33 @@
package com.android.systemui.qs.customize;
import android.content.Context;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.R;
+
import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.tileimpl.QSTileView;
-import java.util.Objects;
public class CustomizeTileView extends QSTileView {
+ private boolean mShowAppLabel;
- private TextView mAppLabel;
- private int mLabelMinLines;
public CustomizeTileView(Context context, QSIconView icon) {
super(context, icon);
}
- @Override
- protected void createLabel() {
- super.createLabel();
- mLabelMinLines = mLabel.getMinLines();
- mAppLabel = findViewById(R.id.app_label);
- mAppLabel.setAlpha(.6f);
- }
-
public void setShowAppLabel(boolean showAppLabel) {
- mAppLabel.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
+ mShowAppLabel = showAppLabel;
+ mSecondLine.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
mLabel.setSingleLine(showAppLabel);
}
- public void setAppLabel(CharSequence label) {
- if (!Objects.equals(label, mAppLabel.getText())) {
- mAppLabel.setText(label);
- }
+ @Override
+ protected void handleStateChanged(QSTile.State state) {
+ super.handleStateChanged(state);
+ mSecondLine.setVisibility(mShowAppLabel ? View.VISIBLE : View.GONE);
}
public TextView getAppLabel() {
- return mAppLabel;
+ return mSecondLine;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 3ba5fe6..441d29b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -274,7 +274,6 @@
R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
}
holder.mTileView.onStateChanged(info.state);
- holder.mTileView.setAppLabel(info.appLabel);
holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
if (mAccessibilityManager.isTouchExplorationEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 2ac592f..8bf4096 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -27,6 +27,7 @@
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.TileService;
+import android.text.TextUtils;
import android.widget.Button;
import com.android.systemui.Dependency;
@@ -169,7 +170,8 @@
info.state.expandedAccessibilityClassName =
Button.class.getName();
info.spec = spec;
- info.appLabel = appLabel;
+ info.state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
+ ? null : appLabel;
info.isSystem = isSystem;
mTiles.add(info);
mSpecs.add(spec);
@@ -186,7 +188,6 @@
public static class TileInfo {
public String spec;
- public CharSequence appLabel;
public QSTile.State state;
public boolean isSystem;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 45c20a0..4774785 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -40,7 +40,7 @@
private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
- private TextView mSecondLine;
+ protected TextView mSecondLine;
private ImageView mPadLock;
private int mState;
private ViewGroup mLabelContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 8c28af5..8b6b5fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -99,7 +99,6 @@
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1);
private final int mTintedRippleColor;
- private final int mLowPriorityRippleColor;
protected final int mNormalRippleColor;
private final AccessibilityManager mAccessibilityManager;
private final DoubleTapHelper mDoubleTapHelper;
@@ -134,7 +133,6 @@
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
private final int mNormalColor;
- private final int mLowPriorityColor;
private boolean mIsBelowSpeedBump;
private FalsingManager mFalsingManager;
@@ -191,12 +189,8 @@
setClipChildren(false);
setClipToPadding(false);
mNormalColor = context.getColor(R.color.notification_material_background_color);
- mLowPriorityColor = context.getColor(
- R.color.notification_material_background_low_priority_color);
mTintedRippleColor = context.getColor(
R.color.notification_ripple_tinted_color);
- mLowPriorityRippleColor = context.getColor(
- R.color.notification_ripple_color_low_priority);
mNormalRippleColor = context.getColor(
R.color.notification_ripple_untinted_color);
mFalsingManager = FalsingManager.getInstance(context);
@@ -997,8 +991,6 @@
}
if (withTint && mBgTint != NO_COLOR) {
return mBgTint;
- } else if (mIsBelowSpeedBump) {
- return mLowPriorityColor;
} else {
return mNormalColor;
}
@@ -1007,8 +999,6 @@
protected int getRippleColor() {
if (mBgTint != 0) {
return mTintedRippleColor;
- } else if (mIsBelowSpeedBump) {
- return mLowPriorityRippleColor;
} else {
return mNormalRippleColor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 9bd8080..6b9567d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -88,6 +88,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
public class ExpandableNotificationRow extends ActivatableNotificationView
implements PluginListener<NotificationMenuRowPlugin> {
@@ -179,6 +180,7 @@
private boolean mExpandAnimationRunning;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
+ private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
private View mHelperButton;
private boolean mChildIsExpanding;
@@ -1115,13 +1117,21 @@
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
boolean wasAboveShelf = isAboveShelf();
+ boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
mHeadsupDisappearRunning = headsUpAnimatingAway;
mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ if (changed && mHeadsUpAnimatingAwayListener != null) {
+ mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
+ }
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
+ public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
+ mHeadsUpAnimatingAwayListener = listener;
+ }
+
/**
* @return if the view was just heads upped and is now animating away. During such a time the
* layout needs to be kept consistent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 48828ab..7a7cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -332,6 +332,7 @@
row.setOnExpandClickListener(mPresenter);
row.setInflationCallback(this);
row.setLongPressListener(getNotificationLongClicker());
+ mListContainer.bindRow(row);
mRemoteInputManager.bindRow(row);
// Get the app name.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 1127075..886d6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -75,7 +75,7 @@
if (shouldApply) {
// lets gray it out
int grey = view.getContext().getColor(
- com.android.internal.R.color.notification_icon_default_color);
+ com.android.internal.R.color.notification_default_color_light);
imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
} else {
// lets reset it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
index 0c19ec0..af9a3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
@@ -188,4 +188,11 @@
default void applyExpandAnimationParams(ExpandAnimationParameters params) {}
default void setExpandingNotification(ExpandableNotificationRow row) {}
+
+ /**
+ * Bind a newly created row.
+ *
+ * @param row The notification to bind.
+ */
+ default void bindRow(ExpandableNotificationRow row) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index aecf5fb..41c7559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -177,7 +177,7 @@
mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
getFullyClosedTranslation());
mShelfState.zTranslation = ambientState.getBaseZHeight();
- if (mAmbientState.isDark()) {
+ if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) {
mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
}
float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 5bb85e2..603902a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -165,7 +165,7 @@
mDensity = context.getResources().getDisplayMetrics().densityDpi;
if (mNotification != null) {
setDecorColor(getContext().getColor(
- com.android.internal.R.color.notification_icon_default_color));
+ com.android.internal.R.color.notification_default_color_light));
}
reloadDimens();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 80854ec..2b7949b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -76,7 +76,6 @@
* notification.
*/
private final Context mPackageContext;
- private boolean mIsLowPriority;
public MediaNotificationProcessor(Context context, Context packageContext) {
this(context, packageContext, new ImageGradientColorizer());
@@ -146,10 +145,7 @@
int foregroundColor = selectForegroundColor(backgroundColor, palette);
builder.setColorPalette(backgroundColor, foregroundColor);
} else {
- int id = mIsLowPriority
- ? R.color.notification_material_background_low_priority_color
- : R.color.notification_material_background_color;
- backgroundColor = mContext.getColor(id);
+ backgroundColor = mContext.getColor(R.color.notification_material_background_color);
}
Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
mContext.getResources().getConfiguration().getLayoutDirection() ==
@@ -307,8 +303,4 @@
private boolean isWhite(float[] hslColor) {
return hslColor[2] >= WHITE_MIN_LIGHTNESS;
}
-
- public void setIsLowPriority(boolean isLowPriority) {
- mIsLowPriority = isLowPriority;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 251e04b..78df77f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -122,9 +122,9 @@
mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
- mColor = resolveColor(mExpandButton);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+ mColor = mNotificationHeader.getOriginalIconColor();
getDozer().setColor(mColor);
}
@@ -132,16 +132,6 @@
mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
}
- private int resolveColor(ImageView icon) {
- if (icon != null && icon.getDrawable() != null) {
- ColorFilter filter = icon.getDrawable().getColorFilter();
- if (filter instanceof PorterDuffColorFilter) {
- return ((PorterDuffColorFilter) filter).getColor();
- }
- }
- return 0;
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index f5110a2d..0143d01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -584,15 +584,9 @@
mSbn.getNotification());
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
- if (mIsLowPriority) {
- int backgroundColor = mContext.getColor(
- R.color.notification_material_background_low_priority_color);
- recoveredBuilder.setBackgroundColorHint(backgroundColor);
- }
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
- processor.setIsLowPriority(mIsLowPriority);
processor.processNotification(notification, recoveredBuilder);
}
return createRemoteViews(mReInflateFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index c576801..9ec5609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -30,6 +30,9 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
@@ -43,11 +46,19 @@
private final HeadsUpStatusBarView mHeadsUpStatusBarView;
private final View mClockView;
private final DarkIconDispatcher mDarkIconDispatcher;
+ private final NotificationPanelView mPanelView;
+ private final Consumer<ExpandableNotificationRow>
+ mSetTrackingHeadsUp = this::setTrackingHeadsUp;
+ private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
+ private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
private float mExpandedHeight;
private boolean mIsExpanded;
private float mExpandFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
+ private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+ -> updatePanelTranslation();
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
@@ -75,18 +86,29 @@
headsUpStatusBarView.setOnDrawingRectChangedListener(
() -> updateIsolatedIconLocation(true /* requireUpdate */));
mStackScroller = stackScroller;
- panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
- panelView.setVerticalTranslationListener(this::updatePanelTranslation);
+ mPanelView = panelView;
+ panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
panelView.setHeadsUpAppearanceController(this);
- mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
- mStackScroller.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
- -> updatePanelTranslation());
+ mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mClockView = clockView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mDarkIconDispatcher.addDarkReceiver(this);
}
+
+ public void destroy() {
+ mHeadsUpManager.removeListener(this);
+ mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+ mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
+ mPanelView.setHeadsUpAppearanceController(null);
+ mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+ mDarkIconDispatcher.removeDarkReceiver(this);
+ }
+
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
mNotificationIconAreaController.setIsolatedIconLocation(
mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 2a1813f..d609ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -60,10 +60,6 @@
private final FalsingManager mFalsingManager;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final Handler mHandler;
- protected KeyguardHostView mKeyguardView;
- protected ViewGroup mRoot;
- private boolean mShowingSoon;
- private int mBouncerPromptReason;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -72,7 +68,14 @@
}
};
private final Runnable mRemoveViewRunnable = this::removeView;
+
private int mStatusBarHeight;
+ private float mExpansion;
+ protected KeyguardHostView mKeyguardView;
+ protected ViewGroup mRoot;
+ private boolean mShowingSoon;
+ private int mBouncerPromptReason;
+ private boolean mIsAnimatingAway;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -252,6 +255,7 @@
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
+ mIsAnimatingAway = false;
if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
if (destroyView) {
@@ -267,6 +271,7 @@
* See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
*/
public void startPreHideAnimation(Runnable runnable) {
+ mIsAnimatingAway = true;
if (mKeyguardView != null) {
mKeyguardView.startDisappearAnimation(runnable);
} else if (runnable != null) {
@@ -290,7 +295,16 @@
}
public boolean isShowing() {
- return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE);
+ return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+ && mExpansion == 0;
+ }
+
+ /**
+ * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
+ * hidden yet, {@code false} otherwise.
+ */
+ public boolean isAnimatingAway() {
+ return mIsAnimatingAway;
}
public void prepare() {
@@ -308,7 +322,8 @@
* @see StatusBarKeyguardViewManager#onPanelExpansionChanged
*/
public void setExpansion(float fraction) {
- if (mKeyguardView != null) {
+ mExpansion = fraction;
+ if (mKeyguardView != null && !mIsAnimatingAway) {
float alpha = MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
mKeyguardView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
mKeyguardView.setTranslationY(fraction * mKeyguardView.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6852df6..64e205d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -247,7 +247,7 @@
private int mStackScrollerMeasuringPass;
private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
= new ArrayList<>();
- private Runnable mVerticalTranslationListener;
+ private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
public NotificationPanelView(Context context, AttributeSet attrs) {
@@ -2360,6 +2360,14 @@
@Override
public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+
+ // When we're unpinning the notification via active edge they remain heads-upped,
+ // we need to make sure that an animation happens in this case, otherwise the notification
+ // will stick to the top without any interaction.
+ if (isFullyCollapsed() && headsUp.isHeadsUp()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
+ headsUp.setHeadsUpIsVisible();
+ }
}
@Override
@@ -2423,8 +2431,9 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
- if (mVerticalTranslationListener != null) {
- mVerticalTranslationListener.run();
+ int size = mVerticalTranslationListener.size();
+ for (int i = 0; i < size; i++) {
+ mVerticalTranslationListener.get(i).run();
}
}
@@ -2736,8 +2745,16 @@
mTrackingHeadsUpListeners.add(listener);
}
- public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener = verticalTranslationListener;
+ public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.remove(listener);
+ }
+
+ public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.add(verticalTranslationListener);
+ }
+
+ public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.remove(verticalTranslationListener);
}
public void setHeadsUpAppearanceController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index b448967..c4d7e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -28,6 +28,7 @@
public static final String TAG = PanelBar.class.getSimpleName();
private static final boolean SPEW = false;
private boolean mBouncerShowing;
+ private boolean mExpanded;
public static final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
@@ -71,10 +72,15 @@
: IMPORTANT_FOR_ACCESSIBILITY_AUTO;
setImportantForAccessibility(important);
+ updateVisibility();
if (mPanel != null) mPanel.setImportantForAccessibility(important);
}
+ private void updateVisibility() {
+ mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ }
+
public boolean panelEnabled() {
return true;
}
@@ -124,7 +130,8 @@
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelView pv = mPanel;
- pv.setVisibility(expanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ mExpanded = expanded;
+ updateVisibility();
// adjust any other panels that may be partially visible
if (expanded) {
if (mState == STATE_CLOSED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 19544b1..9abbfb83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -352,7 +352,7 @@
@Override
public void onDraw(Canvas canvas) {
- if (mNavigationBarView.isQuickScrubEnabled()) {
+ if (!mNavigationBarView.isQuickScrubEnabled()) {
return;
}
int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index feb7dc3..e6a9b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
@@ -209,6 +210,7 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -604,6 +606,8 @@
private View mNavigationBarView;
protected ActivityLaunchAnimator mActivityLaunchAnimator;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private boolean mVibrateOnOpening;
+ private VibratorHelper mVibratorHelper;
@Override
public void start() {
@@ -641,6 +645,9 @@
updateDisplaySize();
Resources res = mContext.getResources();
+ mVibrateOnOpening = mContext.getResources().getBoolean(
+ R.bool.config_vibrateOnIconAnimation);
+ mVibratorHelper = Dependency.get(VibratorHelper.class);
mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
@@ -809,6 +816,10 @@
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ if (mHeadsUpAppearanceController != null) {
+ // This view is being recreated, let's destroy the old one
+ mHeadsUpAppearanceController.destroy();
+ }
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
@@ -2153,6 +2164,9 @@
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mNotificationPanel.isFullyCollapsed()) {
+ if (mVibrateOnOpening) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
mNotificationPanel.expand(true /* animate */);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
} else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a9c467e..56a7b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -150,8 +150,7 @@
mBouncer.setExpansion(expansion);
if (expansion == 1) {
mBouncer.onFullyHidden();
- updateStates();
- } else if (!mBouncer.isShowing()) {
+ } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
mBouncer.show(true /* resetSecuritySelection */, false /* notifyFalsing */);
} else if (noLongerTracking) {
// Notify that falsing manager should stop its session when user stops touching,
@@ -159,6 +158,9 @@
// data.
mBouncer.onFullyShown();
}
+ if (expansion == 0 || expansion == 1) {
+ updateStates();
+ }
}
mLastTracking = tracking;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index b22ce18..0adb439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -64,6 +64,7 @@
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .setUids(null)
.build();
private static final int NO_NETWORK = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
index a36c966..f98b3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -47,6 +47,11 @@
updateRounding(headsUp, true /* animate */);
}
+ public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
+ boolean isAnimatingAway) {
+ updateRounding(row, false /* animate */);
+ }
+
private void updateRounding(ActivatableNotificationView view, boolean animate) {
float topRoundness = getRoundness(view, true /* top */);
float bottomRoundness = getRoundness(view, false /* top */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a572450..dc94203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -109,6 +109,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -3022,6 +3023,12 @@
}
@Override
+ public void bindRow(ExpandableNotificationRow row) {
+ row.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
+ }
+
+ @Override
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
requestChildrenUpdate();
@@ -4525,6 +4532,13 @@
}
/**
+ * Stop a listener from listening to the expandedHeight.
+ */
+ public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ mExpandedHeightListeners.remove(listener);
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
public interface OnEmptySpaceClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e94d6bd..1c8a26c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -451,11 +451,11 @@
toastText = R.string.volume_dialog_ringer_guidance_ring;
break;
case RINGER_MODE_SILENT:
- toastText = R.string.volume_dialog_ringer_guidance_silent;
+ toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
break;
case RINGER_MODE_VIBRATE:
default:
- toastText = R.string.volume_dialog_ringer_guidance_vibrate;
+ toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
}
Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 34e444e..231a1866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -40,17 +40,22 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ExpandableNotificationRowTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
private NotificationTestHelper mNotificationTestHelper;
+ boolean mHeadsUpAnimatingAway = false;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext);
mGroup = mNotificationTestHelper.createGroup();
+ mGroup.setHeadsUpAnimatingAwayListener(
+ animatingAway -> mHeadsUpAnimatingAway = animatingAway);
}
@Test
@@ -195,4 +200,12 @@
mGroup.getAppOpsOnClickListener().onClick(view);
verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
}
+
+ @Test
+ public void testHeadsUpAnimatingAwayListener() {
+ mGroup.setHeadsUpAnimatingAway(true);
+ Assert.assertEquals(true, mHeadsUpAnimatingAway);
+ mGroup.setHeadsUpAnimatingAway(false);
+ Assert.assertEquals(false, mHeadsUpAnimatingAway);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index c904ef3..7a61bfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.phone;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -26,6 +29,7 @@
import android.view.View;
import android.widget.TextView;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.TestableDependency;
import com.android.systemui.statusbar.CommandQueue;
@@ -46,6 +50,10 @@
@RunWith(AndroidJUnit4.class)
public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
+ private final NotificationStackScrollLayout mStackScroller =
+ mock(NotificationStackScrollLayout.class);
+ private final NotificationPanelView mPanelView = mock(NotificationPanelView.class);
+ private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private ExpandableNotificationRow mFirst;
private HeadsUpStatusBarView mHeadsUpStatusBarView;
@@ -55,7 +63,7 @@
public void setUp() throws Exception {
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
- mDependency.injectMockDependency(DarkIconDispatcher.class);
+ mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
mock(TextView.class));
mHeadsUpManager = mock(HeadsUpManagerPhone.class);
@@ -63,8 +71,8 @@
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mHeadsUpStatusBarView,
- mock(NotificationStackScrollLayout.class),
- mock(NotificationPanelView.class),
+ mStackScroller,
+ mPanelView,
new View(mContext));
mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
}
@@ -110,4 +118,20 @@
mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
}
+
+ @Test
+ public void testDestroy() {
+ reset(mHeadsUpManager);
+ reset(mDarkIconDispatcher);
+ reset(mPanelView);
+ reset(mStackScroller);
+ mHeadsUpAppearanceController.destroy();
+ verify(mHeadsUpManager).removeListener(any());
+ verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
+ verify(mPanelView).removeVerticalTranslationListener(any());
+ verify(mPanelView).removeTrackingHeadsUpListener(any());
+ verify(mPanelView).setHeadsUpAppearanceController(any());
+ verify(mStackScroller).removeOnExpandedHeightListener(any());
+ verify(mStackScroller).removeOnLayoutChangeListener(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index f3a8417..a37947d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -22,8 +22,6 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.calls;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -238,9 +236,19 @@
}
@Test
- public void testIsShowing() {
+ public void testIsShowing_animated() {
Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
- mBouncer.show(true);
+ mBouncer.show(true /* reset */);
+ Assert.assertTrue("Should be showing", mBouncer.isShowing());
+ }
+
+ @Test
+ public void testIsShowing_forSwipeUp() {
+ mBouncer.setExpansion(1f);
+ mBouncer.show(true /* reset */, false /* animated */);
+ Assert.assertFalse("Should only be showing after collapsing notification panel",
+ mBouncer.isShowing());
+ mBouncer.setExpansion(0f);
Assert.assertTrue("Should be showing", mBouncer.isShowing());
}
@@ -269,6 +277,25 @@
}
@Test
+ public void testIsHiding_preHideOrHide() {
+ Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway());
+ mBouncer.startPreHideAnimation(null /* runnable */);
+ Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway());
+ mBouncer.hide(false /* destroyView */);
+ Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway());
+ }
+
+ @Test
+ public void testIsHiding_skipsTranslation() {
+ mBouncer.show(false /* reset */);
+ reset(mKeyguardHostView);
+ mBouncer.startPreHideAnimation(null /* runnable */);
+ mBouncer.setExpansion(0.5f);
+ verify(mKeyguardHostView, never()).setTranslationY(anyFloat());
+ verify(mKeyguardHostView, never()).setAlpha(anyFloat());
+ }
+
+ @Test
public void testIsSecure() {
Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 7ca9d73..f76de5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -19,11 +19,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -32,6 +36,9 @@
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.os.UserManager;
import android.security.IKeyChainService;
import android.support.test.runner.AndroidJUnit4;
@@ -61,6 +68,7 @@
private final UserManager mUserManager = mock(UserManager.class);
private SecurityControllerImpl mSecurityController;
private CountDownLatch mStateChangedLatch;
+ private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
// implementing SecurityControllerCallback
@Override
@@ -72,7 +80,7 @@
public void setUp() throws Exception {
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
mContext.addMockSystemService(Context.USER_SERVICE, mUserManager);
- mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
+ mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mConnectivityManager);
Intent intent = new Intent(IKeyChainService.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
@@ -176,4 +184,12 @@
//assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS));
//assertFalse(mSecurityController.hasCACertInCurrentUser());
}
+
+ @Test
+ public void testNetworkRequest() {
+ verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
+ (NetworkRequest request) -> request.networkCapabilities.getUids() == null
+ && request.networkCapabilities.getCapabilities().length == 0
+ ), any(NetworkCallback.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
index 1d2c01d..28d1aff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -46,6 +46,7 @@
import org.junit.runner.RunWith;
import java.util.HashSet;
+import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -61,7 +62,11 @@
public void setUp() throws Exception {
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
+ mFirst.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
mSecond = testHelper.createRow();
+ mSecond.setHeadsUpAnimatingAwayListener(animatingAway
+ -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
@@ -153,4 +158,24 @@
Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
}
+
+ @Test
+ public void testRoundingUpdatedWhenAnimatingAwayTrue() {
+ mRoundnessManager.setExpanded(0.0f, 0.0f);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ mFirst.setHeadsUpAnimatingAway(true);
+ Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+
+ @Test
+ public void testRoundingUpdatedWhenAnimatingAwayFalse() {
+ mRoundnessManager.setExpanded(0.0f, 0.0f);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ mFirst.setHeadsUpAnimatingAway(true);
+ mFirst.setHeadsUpAnimatingAway(false);
+ Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 0763fa1..39d0070 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1134,14 +1134,38 @@
NUM_CLIENTS_CHANGED = 2;
}
+ // Soft AP channel bandwidth types
+ enum ChannelBandwidth {
+
+ BANDWIDTH_INVALID = 0;
+
+ BANDWIDTH_20_NOHT = 1;
+
+ BANDWIDTH_20 = 2;
+
+ BANDWIDTH_40 = 3;
+
+ BANDWIDTH_80 = 4;
+
+ BANDWIDTH_80P80 = 5;
+
+ BANDWIDTH_160 = 6;
+ }
+
// Type of event being recorded
optional SoftApEventType event_type = 1;
- // Absolute time when event happened
+ // Time passed since last boot in milliseconds
optional int64 time_stamp_millis = 2;
// Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
optional int32 num_connected_clients = 3;
+
+ // Channel frequency used for Soft AP
+ optional int32 channel_frequency = 4;
+
+ // Channel bandwidth used for Soft AP
+ optional ChannelBandwidth channel_bandwidth = 5;
}
// Wps connection metrics
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 7409ec2..009e723 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -80,6 +80,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.autofill.AutofillManagerService.PackageCompatState;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.FileDescriptor;
@@ -108,10 +109,6 @@
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
- // TODO(b/74445943): temporary work around until P Development Preview 3 is branched
- private static final List<String> DEFAULT_BUTTONS = Arrays.asList("url_bar",
- "location_bar_edit_text");
-
private final Context mContext;
private final AutoFillUI mUi;
@@ -600,7 +597,7 @@
final List<String> urlBarIds;
if (urlBlockIndex == -1) {
packageName = packageBlock;
- urlBarIds = DEFAULT_BUTTONS; // TODO(b/74445943): back to null
+ urlBarIds = null;
} else {
if (packageBlock.charAt(packageBlock.length() - 1)
!= COMPAT_PACKAGE_URL_IDS_BLOCK_END) {
@@ -655,7 +652,7 @@
/**
* Compatibility mode metadata per package.
*/
- private static final class PackageCompatState {
+ static final class PackageCompatState {
private final long maxVersionCode;
private final String[] urlBarResourceIds;
@@ -666,8 +663,8 @@
@Override
public String toString() {
- return "PackageCompatState: [maxVersionCode=" + maxVersionCode
- + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds) + "]";
+ return "maxVersionCode=" + maxVersionCode
+ + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds);
}
}
@@ -756,6 +753,25 @@
}
}
}
+
+ private void dump(String prefix, PrintWriter pw) {
+ if (mUserSpecs == null) {
+ pw.println("N/A");
+ return;
+ }
+ pw.println();
+ final String prefix2 = prefix + " ";
+ for (int i = 0; i < mUserSpecs.size(); i++) {
+ final int user = mUserSpecs.keyAt(i);
+ pw.print(prefix); pw.print("User: "); pw.println(user);
+ final ArrayMap<String,PackageCompatState> perUser = mUserSpecs.get(i);
+ for (int j = 0; j < perUser.size(); j++) {
+ final String packageName = perUser.keyAt(j);
+ final PackageCompatState state = perUser.valueAt(j);
+ pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+ }
+ }
+ }
}
final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
@@ -1121,6 +1137,7 @@
boolean oldDebug = sDebug;
final String prefix = " ";
+ final String prefix2 = " ";
try {
synchronized (mLock) {
oldDebug = sDebug;
@@ -1145,8 +1162,8 @@
}
mUi.dump(pw);
pw.print("Autofill Compat State: ");
- pw.println(mAutofillCompatState.mUserSpecs);
- pw.print(prefix); pw.print("from settings: ");
+ mAutofillCompatState.dump(prefix2, pw);
+ pw.print(prefix2); pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
}
if (showHistory) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 54ea3ba..ef0d7e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1189,7 +1189,7 @@
boolean isFieldClassificationEnabledLocked() {
return Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0,
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
mUserId) == 1;
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 5c41f3f..7bb532e 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -26,6 +26,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -107,6 +108,13 @@
}
@NonNull
+ public static String paramsToString(@NonNull WindowManager.LayoutParams params) {
+ final StringBuilder builder = new StringBuilder(25);
+ params.dumpDimensions(builder);
+ return builder.toString();
+ }
+
+ @NonNull
static ArrayMap<AutofillId, AutofillValue> getFields(@NonNull Dataset dataset) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
final ArrayList<AutofillValue> values = dataset.getFieldValues();
@@ -128,7 +136,7 @@
return log;
}
- public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) {
+ public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable CharSequence text) {
if (text == null) {
pw.println("null");
} else {
@@ -173,6 +181,7 @@
* @param structure Assist structure
* @param urlBarIds list of ids; only the first id found will be sanitized.
*/
+ @Nullable
public static void sanitizeUrlBar(@NonNull AssistStructure structure,
@NonNull String[] urlBarIds) {
final ViewNode urlBarNode = findViewNode(structure, (node) -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 55c0372..1e1de35 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2077,8 +2077,11 @@
if (saveTriggerId != null) {
writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
}
- saveOnAllViewsInvisible =
- (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+ int flags = saveInfo.getFlags();
+ if (mCompatMode) {
+ flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
+ }
+ saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
// We only need to track views if we want to save once they become invisible.
if (saveOnAllViewsInvisible) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 0dbdc13..03c5850 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -206,29 +206,51 @@
@Override
public String toString() {
- return "ViewState: [id=" + id + ", datasetId=" + mDatasetId
- + ", currentValue=" + mCurrentValue
- + ", autofilledValue=" + mAutofilledValue
- + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
+ final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
+ if (mDatasetId != null) {
+ builder.append("datasetId:" ).append(mDatasetId);
+ }
+ builder.append("state:" ).append(getStateAsString());
+ if (mCurrentValue != null) {
+ builder.append("currentValue:" ).append(mCurrentValue);
+ }
+ if (mAutofilledValue != null) {
+ builder.append("autofilledValue:" ).append(mAutofilledValue);
+ }
+ if (mSanitizedValue != null) {
+ builder.append("sanitizedValue:" ).append(mSanitizedValue);
+ }
+ if (mVirtualBounds != null) {
+ builder.append("virtualBounds:" ).append(mVirtualBounds);
+ }
+ return builder.toString();
}
void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("id:" ); pw.println(this.id);
- pw.print(prefix); pw.print("datasetId:" ); pw.println(this.mDatasetId);
+ pw.print(prefix); pw.print("id:" ); pw.println(id);
+ if (mDatasetId != null) {
+ pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
+ }
pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
- pw.print(prefix); pw.print("response:");
- if (mResponse == null) {
- pw.println("N/A");
- } else {
+ if (mResponse != null) {
+ pw.print(prefix); pw.print("response:");
if (sVerbose) {
pw.println(mResponse);
} else {
- pw.println(mResponse.getRequestId());
+ pw.print("id=");pw.println(mResponse.getRequestId());
}
}
- pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
- pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
- pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
- pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+ if (mCurrentValue != null) {
+ pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
+ }
+ if (mAutofilledValue != null) {
+ pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+ }
+ if (mSanitizedValue != null) {
+ pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
+ }
+ if (mVirtualBounds != null) {
+ pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+ }
}
}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index ef4656b..edfc412 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -15,6 +15,7 @@
*/
package com.android.server.autofill.ui;
+import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -37,7 +38,6 @@
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -568,12 +568,24 @@
@Override
public String toString() {
- return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId())
- + ", value=" + (value == null ? "null" : value.length() + "_chars")
- + ", filterable=" + filterable
- + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars")
- + ", view=" + view.getAutofillId()
- + "]";
+ final StringBuilder builder = new StringBuilder("ViewItem:[view=")
+ .append(view.getAutofillId());
+ final String datasetId = dataset == null ? null : dataset.getId();
+ if (datasetId != null) {
+ builder.append(", dataset=").append(datasetId);
+ }
+ if (value != null) {
+ // Cannot print value because it could contain PII
+ builder.append(", value=").append(value.length()).append("_chars");
+ }
+ if (filterable) {
+ builder.append(", filterable");
+ }
+ if (filter != null) {
+ // Filter should not have PII, but it could be a huge regexp
+ builder.append(", filter=").append(filter.pattern().length()).append("_chars");
+ }
+ return builder.append(']').toString();
}
}
@@ -583,8 +595,7 @@
boolean fitsSystemWindows, int layoutDirection) {
if (sVerbose) {
Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
- + ", epicenter="+ transitionEpicenter + ", dir=" + layoutDirection
- + ", params=" + p);
+ + ", params=" + paramsToString(p));
}
UiThread.getHandler().post(() -> mWindow.show(p));
}
@@ -616,7 +627,9 @@
* Shows the window.
*/
public void show(WindowManager.LayoutParams params) {
- if (sVerbose) Slog.v(TAG, "show(): showing=" + mShowing + ", params="+ params);
+ if (sVerbose) {
+ Slog.v(TAG, "show(): showing=" + mShowing + ", params=" + paramsToString(params));
+ }
try {
// Okay here is a bit of voodoo - we want to show the window as system
// controlled one so it covers app windows - adjust the params accordingly.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 30d31b8..088f366 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1386,6 +1386,12 @@
}
}
+ private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
+ if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+ nc.addCapability(NET_CAPABILITY_FOREGROUND);
+ }
+ }
+
@Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
@@ -4411,15 +4417,13 @@
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
restrictRequestUidsForCaller(nc);
- if (!ConnectivityManager.checkChangePermission(mContext)) {
- // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
- // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
- // onLost and onAvailable callbacks when networks move in and out of the background.
- // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
- // can't request networks.
- nc.addCapability(NET_CAPABILITY_FOREGROUND);
- }
- ensureValidNetworkSpecifier(networkCapabilities);
+ // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
+ // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
+ // onLost and onAvailable callbacks when networks move in and out of the background.
+ // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
+ // can't request networks.
+ restrictBackgroundRequestForCaller(nc);
+ ensureValidNetworkSpecifier(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 457564b..955035b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21798,6 +21798,8 @@
"com.android.frameworks.locationtests",
"com.android.frameworks.coretests.privacy",
"com.android.settings.ui",
+ "com.android.perftests.core",
+ "com.android.perftests.multiuser",
};
public boolean startInstrumentation(ComponentName className,
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index f5e1a31..2291e44 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -283,30 +283,34 @@
mUseLatestStates = true;
}
- synchronized (mWorkerLock) {
- if (DEBUG) {
- Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
- }
- try {
- updateExternalStatsLocked(reason, updateFlags, onBattery,
- onBatteryScreenOff, useLatestStates);
- } finally {
+ try {
+ synchronized (mWorkerLock) {
if (DEBUG) {
- Slog.d(TAG, "end updateExternalStatsSync");
+ Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
+ }
+ try {
+ updateExternalStatsLocked(reason, updateFlags, onBattery,
+ onBatteryScreenOff, useLatestStates);
+ } finally {
+ if (DEBUG) {
+ Slog.d(TAG, "end updateExternalStatsSync");
+ }
}
}
- }
- if ((updateFlags & UPDATE_CPU) != 0) {
- mStats.copyFromAllUidsCpuTimes();
- }
-
- // Clean up any UIDs if necessary.
- synchronized (mStats) {
- for (int uid : uidsToRemove) {
- mStats.removeIsolatedUidLocked(uid);
+ if ((updateFlags & UPDATE_CPU) != 0) {
+ mStats.copyFromAllUidsCpuTimes();
}
- mStats.clearPendingRemovedUids();
+
+ // Clean up any UIDs if necessary.
+ synchronized (mStats) {
+ for (int uid : uidsToRemove) {
+ mStats.removeIsolatedUidLocked(uid);
+ }
+ mStats.clearPendingRemovedUids();
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error updating external stats: ", e);
}
}
};
@@ -398,7 +402,7 @@
if (bluetoothInfo.isValid()) {
mStats.updateBluetoothStateLocked(bluetoothInfo);
} else {
- Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
+ Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
}
}
}
@@ -410,7 +414,7 @@
if (wifiInfo.isValid()) {
mStats.updateWifiState(extractDeltaLocked(wifiInfo));
} else {
- Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
+ Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
}
@@ -418,7 +422,7 @@
if (modemInfo.isValid()) {
mStats.updateMobileRadioState(modemInfo);
} else {
- Slog.e(TAG, "modem info is invalid: " + modemInfo);
+ Slog.w(TAG, "modem info is invalid: " + modemInfo);
}
}
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fc8b624..1335ced 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -508,6 +508,10 @@
&& tr.userId == userId
&& tr.realActivitySuspended != suspended) {
tr.realActivitySuspended = suspended;
+ if (suspended) {
+ mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ REMOVE_FROM_RECENTS, "suspended-package");
+ }
notifyTaskPersisterLocked(tr, false);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c036549..041764f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -24,6 +24,9 @@
import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioManager.STREAM_SYSTEM;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
+import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
+import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
import android.Manifest;
import android.annotation.NonNull;
@@ -108,6 +111,7 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -124,6 +128,7 @@
import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
@@ -440,6 +445,12 @@
// Is there a vibrator
private final boolean mHasVibrator;
+ // Used to play vibrations
+ private Vibrator mVibrator;
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build();
// Broadcast receiver for device connections intent broadcasts
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
@@ -697,8 +708,8 @@
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
- Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
+ mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
// Initialize volume
int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1);
@@ -1649,7 +1660,7 @@
// Check if volume update should be send to Hearing Aid
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- setHearingAidVolume(newIndex);
+ setHearingAidVolume(newIndex, streamType);
}
// Check if volume update should be sent to Hdmi system audio.
@@ -1898,7 +1909,7 @@
}
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- setHearingAidVolume(index);
+ setHearingAidVolume(index, streamType);
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
@@ -2423,6 +2434,54 @@
setRingerMode(ringerMode, caller, false /*external*/);
}
+ public void silenceRingerModeInternal(String reason) {
+ VibrationEffect effect = null;
+ int ringerMode = AudioManager.RINGER_MODE_SILENT;
+ int toastText = 0;
+
+ int silenceRingerSetting = Settings.Secure.VOLUME_HUSH_OFF;
+ if (mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
+ silenceRingerSetting = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
+ UserHandle.USER_CURRENT);
+ }
+
+ switch(silenceRingerSetting) {
+ case VOLUME_HUSH_MUTE:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ ringerMode = AudioManager.RINGER_MODE_SILENT;
+ toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
+ break;
+ case VOLUME_HUSH_VIBRATE:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+ ringerMode = AudioManager.RINGER_MODE_VIBRATE;
+ toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
+ break;
+ }
+ maybeVibrate(effect);
+ setRingerModeInternal(ringerMode, reason);
+ Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
+ }
+
+ private boolean maybeVibrate(VibrationEffect effect) {
+ if (!mHasVibrator) {
+ return false;
+ }
+ final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
+ if (hapticsDisabled) {
+ return false;
+ }
+
+ if (effect == null) {
+ return false;
+ }
+ mVibrator.vibrate(
+ Binder.getCallingUid(), mContext.getOpPackageName(), effect, VIBRATION_ATTRIBUTES);
+ return true;
+ }
+
private void setRingerMode(int ringerMode, String caller, boolean external) {
if (mUseFixedVolume || mIsSingleVolume) {
return;
@@ -2484,14 +2543,24 @@
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
- final boolean ringerModeMute = mRingerMode == AudioManager.RINGER_MODE_VIBRATE
- || mRingerMode == AudioManager.RINGER_MODE_SILENT;
+ final int ringerMode = mRingerMode; // Read ringer mode as reading primitives is atomic
+ final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+ || ringerMode == AudioManager.RINGER_MODE_SILENT;
+ final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+ && isBluetoothScoOn();
+ // Ask audio policy engine to force use Bluetooth SCO channel if needed
+ final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid();
+ sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_VIBRATE_RINGING,
+ shouldRingSco ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE, eventSource, 0);
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
+ final boolean muteAllowedBySco =
+ !(shouldRingSco && streamType == AudioSystem.STREAM_RING);
final boolean shouldZenMute = shouldZenMuteStream(streamType);
final boolean shouldMute = shouldZenMute || (ringerModeMute
- && isStreamAffectedByRingerMode(streamType));
+ && isStreamAffectedByRingerMode(streamType) && muteAllowedBySco);
if (isMuted == shouldMute) continue;
if (!shouldMute) {
// unmute
@@ -3132,6 +3201,8 @@
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource, 0);
+ // Un-mute ringtone stream volume
+ setRingerModeInt(getRingerModeInternal(), false);
}
/** @see AudioManager#isBluetoothScoOn() */
@@ -4724,7 +4795,7 @@
}
public boolean setIndex(int index, int device, String caller) {
- boolean changed = false;
+ boolean changed;
int oldIndex;
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
@@ -4741,7 +4812,7 @@
// - there is no volume index stored for this device on alias stream.
// If changing volume of current device, also change volume of current
// device on aliased stream
- final boolean currentDevice = (device == getDeviceForStream(mStreamType));
+ final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
final int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState aliasStreamState = mStreamStates[streamType];
@@ -4750,12 +4821,22 @@
(changed || !aliasStreamState.hasIndexForDevice(device))) {
final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
aliasStreamState.setIndex(scaledIndex, device, caller);
- if (currentDevice) {
+ if (isCurrentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller);
}
}
}
+ // Mirror changes in SPEAKER ringtone volume on SCO when
+ if (changed && mStreamType == AudioSystem.STREAM_RING
+ && device == AudioSystem.DEVICE_OUT_SPEAKER) {
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ int otherDevice = mIndexMap.keyAt(i);
+ if ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
+ mIndexMap.put(otherDevice, index);
+ }
+ }
+ }
}
}
if (changed) {
@@ -5606,11 +5687,11 @@
makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
}
- private void setHearingAidVolume(int index) {
+ private void setHearingAidVolume(int index, int streamType) {
synchronized (mHearingAidLock) {
if (mHearingAid != null) {
//hearing aid expect volume value in range -128dB to 0dB
- int gainDB = (int)AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index/10,
+ int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10,
AudioSystem.DEVICE_OUT_HEARING_AID);
if (gainDB < BT_HEARING_AID_GAIN_MIN)
gainDB = BT_HEARING_AID_GAIN_MIN;
@@ -5622,7 +5703,7 @@
// must be called synchronized on mConnectedDevices
private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
- setHearingAidVolume(index);
+ setHearingAidVolume(index, AudioSystem.STREAM_MUSIC);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
@@ -5868,7 +5949,8 @@
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
- AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
+ AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE |
+ AudioSystem.DEVICE_OUT_HEARING_AID;
// must be called before removing the device from mConnectedDevices
// Called synchronized on mConnectedDevices
@@ -7246,6 +7328,11 @@
}
@Override
+ public void silenceRingerModeInternal(String caller) {
+ AudioService.this.silenceRingerModeInternal(caller);
+ }
+
+ @Override
public void updateRingerModeAffectedStreamsInternal() {
synchronized (mSettingsLock) {
if (updateRingerAndZenModeAffectedStreams()) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index e084ff8..d578e95 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -27,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +41,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -150,7 +154,14 @@
update(mUsers, mApps, true);
}
- private boolean hasPermission(PackageInfo app, String permission) {
+ @VisibleForTesting
+ boolean isPreinstalledSystemApp(PackageInfo app) {
+ int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
+ return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
+ }
+
+ @VisibleForTesting
+ boolean hasPermission(PackageInfo app, String permission) {
if (app.requestedPermissions != null) {
for (String p : app.requestedPermissions) {
if (permission.equals(p)) {
@@ -166,14 +177,40 @@
}
private boolean hasRestrictedNetworkPermission(PackageInfo app) {
- int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
- if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
- return true;
- }
+ if (isPreinstalledSystemApp(app)) return true;
return hasPermission(app, CONNECTIVITY_INTERNAL)
|| hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
+ private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
+ // This function defines what it means to hold the permission to use
+ // background networks.
+ return hasPermission(app, CHANGE_NETWORK_STATE)
+ || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
+ || hasPermission(app, CONNECTIVITY_INTERNAL)
+ || hasPermission(app, NETWORK_STACK)
+ // TODO : remove this check (b/31479477). Not all preinstalled apps should
+ // have access to background networks, they should just request the appropriate
+ // permission for their use case from the list above.
+ || isPreinstalledSystemApp(app);
+ }
+
+ public boolean hasUseBackgroundNetworksPermission(int uid) {
+ final String[] names = mPackageManager.getPackagesForUid(uid);
+ if (null == names || names.length == 0) return false;
+ try {
+ // Only using the first package name. There may be multiple names if multiple
+ // apps share the same UID, but in that case they also share permissions so
+ // querying with any of the names will return the same results.
+ final PackageInfo app = mPackageManager.getPackageInfo(names[0], GET_PERMISSIONS);
+ return hasUseBackgroundNetworksPermission(app);
+ } catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + names[0], e);
+ return false;
+ }
+ }
+
private int[] toIntArray(List<Integer> list) {
int[] array = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
@@ -308,4 +345,8 @@
private static void loge(String s) {
Log.e(TAG, s);
}
+
+ private static void loge(String s, Throwable e) {
+ Log.e(TAG, s, e);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a87a113..b5eb8bf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2033,13 +2033,7 @@
public int[] getPendingRecoverySecretTypes() throws RemoteException {
throw new SecurityException("Not implemented");
}
-
- @Override
- public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
- throws RemoteException {
- mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
- }
-
+
@Override
public byte[] startRecoverySession(@NonNull String sessionId,
@NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index a7d32ed..57fb74d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -61,8 +61,6 @@
private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
- private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
/**
* Encrypts the recovery key using both the lock screen hash and the remote storage's public
@@ -298,8 +296,12 @@
*/
public static byte[] packVaultParams(
PublicKey thmPublicKey, long counterId, int maxAttempts, byte[] vaultHandle) {
- // TODO: Check if vaultHandle has exactly the length of VAULT_HANDLE_LENGTH_BYTES somewhere
- return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES)
+ int vaultParamsLength
+ = 65 // public key
+ + 8 // counterId
+ + 4 // maxAttempts
+ + vaultHandle.length;
+ return ByteBuffer.allocate(vaultParamsLength)
.order(ByteOrder.LITTLE_ENDIAN)
.put(SecureBox.encodePublicKey(thmPublicKey))
.putLong(counterId)
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index e03e86f1..e75722a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -47,6 +47,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.internal.util.Preconditions;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.certificate.SigXml;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
@@ -62,6 +63,7 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException;
@@ -221,6 +223,7 @@
if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ mDatabase.setCounterId(userId, uid, new SecureRandom().nextLong());
}
} catch (CertificateEncodingException e) {
Log.e(TAG, "Failed to encode CertPath", e);
@@ -244,11 +247,11 @@
@NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile,
@NonNull byte[] recoveryServiceSigFile)
throws RemoteException {
- if (recoveryServiceCertFile == null || recoveryServiceSigFile == null) {
- Log.d(TAG, "The given cert or sig file is null");
- throw new ServiceSpecificException(
- ERROR_BAD_CERTIFICATE_FORMAT, "The given cert or sig file is null.");
+ if (rootCertificateAlias == null) {
+ Log.e(TAG, "rootCertificateAlias is null");
}
+ Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
+ Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
SigXml sigXml;
try {
@@ -291,11 +294,11 @@
/**
* Gets all data necessary to recover application keys on new device.
*
- * @return recovery data
+ * @return KeyChain Snapshot.
+ * @throws ServiceSpecificException if no snapshot is pending.
* @hide
*/
- public @NonNull
- KeyChainSnapshot getKeyChainSnapshot()
+ public @NonNull KeyChainSnapshot getKeyChainSnapshot()
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
@@ -325,7 +328,7 @@
throw new UnsupportedOperationException();
}
- public void setServerParams(byte[] serverParams) throws RemoteException {
+ public void setServerParams(@NonNull byte[] serverParams) throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
@@ -338,8 +341,9 @@
/**
* Sets the recovery status of key with {@code alias} to {@code status}.
*/
- public void setRecoveryStatus(String alias, int status) throws RemoteException {
+ public void setRecoveryStatus(@NonNull String alias, int status) throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(alias, "alias is null");
mDatabase.setRecoveryStatus(Binder.getCallingUid(), alias, status);
}
@@ -364,6 +368,7 @@
@NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(secretTypes, "secretTypes is null");
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
@@ -495,7 +500,14 @@
@NonNull List<KeyChainProtectionParams> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
-
+ if (rootCertificateAlias == null) {
+ Log.e(TAG, "rootCertificateAlias is null");
+ }
+ Preconditions.checkNotNull(sessionId, "invalid session");
+ Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
+ Preconditions.checkNotNull(vaultParams, "vaultParams is null");
+ Preconditions.checkNotNull(vaultChallenge, "vaultChallenge is null");
+ Preconditions.checkNotNull(secrets, "secrets is null");
CertPath certPath;
try {
certPath = verifierCertPath.getCertPath();
@@ -541,6 +553,9 @@
@NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(sessionId, "invalid session");
+ Preconditions.checkNotNull(encryptedRecoveryKey, "encryptedRecoveryKey is null");
+ Preconditions.checkNotNull(applicationKeys, "encryptedRecoveryKey is null");
int uid = Binder.getCallingUid();
RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
if (sessionEntry == null) {
@@ -667,10 +682,13 @@
* Destroys the session with the given {@code sessionId}.
*/
public void closeSession(@NonNull String sessionId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ Preconditions.checkNotNull(sessionId, "invalid session");
mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId);
}
public void removeKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -688,6 +706,7 @@
* @return grant alias, which caller can use to access the key.
*/
public String generateKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -726,8 +745,9 @@
*/
public String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
throws RemoteException {
- if (keyBytes == null ||
- keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
+ Preconditions.checkNotNull(alias, "alias is null");
+ Preconditions.checkNotNull(keyBytes, "keyBytes is null");
+ if (keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
Log.e(TAG, "The given key for import doesn't have the required length "
+ RecoverableKeyGenerator.KEY_SIZE_BITS);
throw new ServiceSpecificException(ERROR_INVALID_KEY_FORMAT,
@@ -770,6 +790,7 @@
* @return grant alias, which caller can use to access the key.
*/
public String getKey(@NonNull String alias) throws RemoteException {
+ Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
return getAlias(userId, uid, alias);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
deleted file mode 100644
index 52381b8..0000000
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.locksettings.recoverablekeystore.storage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class provides helper methods serialize and deserialize {@link KeyChainSnapshot}.
- *
- * <p> It is necessary since {@link android.os.Parcelable} is not designed for persistent storage.
- *
- * <p> For every list, length is stored before the elements.
- *
- */
-public class PersistentKeyChainSnapshot {
- private static final int VERSION = 1;
- private static final int NULL_LIST_LENGTH = -1;
-
- private DataInputStream mInput;
- private DataOutputStream mOut;
- private ByteArrayOutputStream mOutStream;
-
- @VisibleForTesting
- PersistentKeyChainSnapshot() {
- }
-
- @VisibleForTesting
- void initReader(byte[] input) {
- mInput = new DataInputStream(new ByteArrayInputStream(input));
- }
-
- @VisibleForTesting
- void initWriter() {
- mOutStream = new ByteArrayOutputStream();
- mOut = new DataOutputStream(mOutStream);
- }
-
- @VisibleForTesting
- byte[] getOutput() {
- return mOutStream.toByteArray();
- }
-
- /**
- * Converts {@link KeyChainSnapshot} to its binary representation.
- *
- * @param snapshot The snapshot.
- *
- * @throws IOException if serialization failed.
- */
- public static byte[] serialize(@NonNull KeyChainSnapshot snapshot) throws IOException {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeInt(VERSION);
- writer.writeKeyChainSnapshot(snapshot);
- return writer.getOutput();
- }
-
- /**
- * deserializes {@link KeyChainSnapshot}.
- *
- * @input input - byte array produced by {@link serialize} method.
- * @throws IOException if parsing failed.
- */
- public static @NonNull KeyChainSnapshot deserialize(@NonNull byte[] input)
- throws IOException {
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(input);
- try {
- int version = reader.readInt();
- if (version != VERSION) {
- throw new IOException("Unsupported version " + version);
- }
- return reader.readKeyChainSnapshot();
- } catch (IOException e) {
- throw new IOException("Malformed KeyChainSnapshot", e);
- }
- }
-
- /**
- * Must be in sync with {@link KeyChainSnapshot.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyChainSnapshot(KeyChainSnapshot snapshot) throws IOException {
- writeInt(snapshot.getSnapshotVersion());
- writeProtectionParamsList(snapshot.getKeyChainProtectionParams());
- writeBytes(snapshot.getEncryptedRecoveryKeyBlob());
- writeKeysList(snapshot.getWrappedApplicationKeys());
-
- writeInt(snapshot.getMaxAttempts());
- writeLong(snapshot.getCounterId());
- writeBytes(snapshot.getServerParams());
- writeBytes(snapshot.getTrustedHardwarePublicKey());
- }
-
- @VisibleForTesting
- KeyChainSnapshot readKeyChainSnapshot() throws IOException {
- int snapshotVersion = readInt();
- List<KeyChainProtectionParams> protectionParams = readProtectionParamsList();
- byte[] encryptedRecoveryKey = readBytes();
- List<WrappedApplicationKey> keysList = readKeysList();
-
- int maxAttempts = readInt();
- long conterId = readLong();
- byte[] serverParams = readBytes();
- byte[] trustedHardwarePublicKey = readBytes();
-
- return new KeyChainSnapshot.Builder()
- .setSnapshotVersion(snapshotVersion)
- .setKeyChainProtectionParams(protectionParams)
- .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
- .setWrappedApplicationKeys(keysList)
- .setMaxAttempts(maxAttempts)
- .setCounterId(conterId)
- .setServerParams(serverParams)
- .setTrustedHardwarePublicKey(trustedHardwarePublicKey)
- .build();
- }
-
- @VisibleForTesting
- void writeProtectionParamsList(
- @NonNull List<KeyChainProtectionParams> ProtectionParamsList) throws IOException {
- writeInt(ProtectionParamsList.size());
- for (KeyChainProtectionParams protectionParams : ProtectionParamsList) {
- writeProtectionParams(protectionParams);
- }
- }
-
- @VisibleForTesting
- List<KeyChainProtectionParams> readProtectionParamsList() throws IOException {
- int length = readInt();
- List<KeyChainProtectionParams> result = new ArrayList<>(length);
- for (int i = 0; i < length; i++) {
- result.add(readProtectionParams());
- }
- return result;
- }
-
- /**
- * Must be in sync with {@link KeyChainProtectionParams.writeToParcel}
- */
- @VisibleForTesting
- void writeProtectionParams(@NonNull KeyChainProtectionParams protectionParams)
- throws IOException {
- if (!ArrayUtils.isEmpty(protectionParams.getSecret())) {
- // Extra security check.
- throw new RuntimeException("User generated secret should not be stored");
- }
- writeInt(protectionParams.getUserSecretType());
- writeInt(protectionParams.getLockScreenUiFormat());
- writeKeyDerivationParams(protectionParams.getKeyDerivationParams());
- writeBytes(protectionParams.getSecret());
- }
-
- @VisibleForTesting
- KeyChainProtectionParams readProtectionParams() throws IOException {
- int userSecretType = readInt();
- int lockScreenUiFormat = readInt();
- KeyDerivationParams derivationParams = readKeyDerivationParams();
- byte[] secret = readBytes();
- return new KeyChainProtectionParams.Builder()
- .setUserSecretType(userSecretType)
- .setLockScreenUiFormat(lockScreenUiFormat)
- .setKeyDerivationParams(derivationParams)
- .setSecret(secret)
- .build();
- }
-
- /**
- * Must be in sync with {@link KeyDerivationParams.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyDerivationParams(@NonNull KeyDerivationParams Params) throws IOException {
- writeInt(Params.getAlgorithm());
- writeBytes(Params.getSalt());
- }
-
- @VisibleForTesting
- KeyDerivationParams readKeyDerivationParams() throws IOException {
- int algorithm = readInt();
- byte[] salt = readBytes();
- return KeyDerivationParams.createSha256Params(salt);
- }
-
- @VisibleForTesting
- void writeKeysList(@NonNull List<WrappedApplicationKey> applicationKeys) throws IOException {
- writeInt(applicationKeys.size());
- for (WrappedApplicationKey keyEntry : applicationKeys) {
- writeKeyEntry(keyEntry);
- }
- }
-
- @VisibleForTesting
- List<WrappedApplicationKey> readKeysList() throws IOException {
- int length = readInt();
- List<WrappedApplicationKey> result = new ArrayList<>(length);
- for (int i = 0; i < length; i++) {
- result.add(readKeyEntry());
- }
- return result;
- }
-
- /**
- * Must be in sync with {@link WrappedApplicationKey.writeToParcel}
- */
- @VisibleForTesting
- void writeKeyEntry(@NonNull WrappedApplicationKey keyEntry) throws IOException {
- mOut.writeUTF(keyEntry.getAlias());
- writeBytes(keyEntry.getEncryptedKeyMaterial());
- writeBytes(keyEntry.getAccount());
- }
-
- @VisibleForTesting
- WrappedApplicationKey readKeyEntry() throws IOException {
- String alias = mInput.readUTF();
- byte[] keyMaterial = readBytes();
- byte[] account = readBytes();
- return new WrappedApplicationKey.Builder()
- .setAlias(alias)
- .setEncryptedKeyMaterial(keyMaterial)
- .setAccount(account)
- .build();
- }
-
- @VisibleForTesting
- void writeInt(int value) throws IOException {
- mOut.writeInt(value);
- }
-
- @VisibleForTesting
- int readInt() throws IOException {
- return mInput.readInt();
- }
-
- @VisibleForTesting
- void writeLong(long value) throws IOException {
- mOut.writeLong(value);
- }
-
- @VisibleForTesting
- long readLong() throws IOException {
- return mInput.readLong();
- }
-
- @VisibleForTesting
- void writeBytes(@Nullable byte[] value) throws IOException {
- if (value == null) {
- writeInt(NULL_LIST_LENGTH);
- return;
- }
- writeInt(value.length);
- mOut.write(value, 0, value.length);
- }
-
- /**
- * Reads @code{byte[]} from current position. Converts {@code null} to an empty array.
- */
- @VisibleForTesting
- @NonNull byte[] readBytes() throws IOException {
- int length = readInt();
- if (length == NULL_LIST_LENGTH) {
- return new byte[]{};
- }
- byte[] result = new byte[length];
- mInput.read(result, 0, result.length);
- return result;
- }
-}
-
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index bda2ed3..2c3d3ab 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -160,7 +160,6 @@
/**
* Type of secret used to generate recovery key. One of
* {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}.
*/
static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3b5b1bf..7348b84 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -608,7 +608,7 @@
*/
private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
int resolvedUserId) {
- if (isCurrentVolumeController(uid, pid)) return;
+ if (isCurrentVolumeController(pid, uid)) return;
if (getContext()
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
@@ -618,13 +618,13 @@
}
}
- private boolean isCurrentVolumeController(int uid, int pid) {
+ private boolean isCurrentVolumeController(int pid, int uid) {
return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
pid, uid) == PackageManager.PERMISSION_GRANTED;
}
private void enforceSystemUiPermission(String action, int pid, int uid) {
- if (!isCurrentVolumeController(uid, pid)) {
+ if (!isCurrentVolumeController(pid, uid)) {
throw new SecurityException("Only system ui may " + action);
}
}
@@ -1501,53 +1501,21 @@
* Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
* permission or an enabled notification listener)
*
- * @param uid uid of the controller app
- * @param packageName package name of the controller app
+ * @param controllerPackageName package name of the controller app
+ * @param controllerPid pid of the controller app
+ * @param controllerUid uid of the controller app
*/
@Override
- public boolean isTrusted(int uid, String packageName) throws RemoteException {
+ public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
+ throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- int userId = UserHandle.getUserId(uid);
- // Sanity check whether uid and packageName matches
- if (uid != mPackageManager.getPackageUid(packageName, 0, userId)) {
- throw new IllegalArgumentException("uid=" + uid + " and packageName="
- + packageName + " doesn't match");
- }
-
- // Check if it's system server or has MEDIA_CONTENT_CONTROL.
- // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
- // check here.
- if (uid == Process.SYSTEM_UID || mPackageManager.checkPermission(
- android.Manifest.permission.MEDIA_CONTENT_CONTROL, packageName, uid)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted"
- + " MEDIA_CONTENT_CONTROL");
- }
-
- // TODO(jaewan): Add hasEnabledNotificationListener(String pkgName) for
- // optimization (Post-P)
- final List<ComponentName> enabledNotificationListeners =
- mNotificationManager.getEnabledNotificationListeners(userId);
- if (enabledNotificationListeners != null) {
- for (int i = 0; i < enabledNotificationListeners.size(); i++) {
- if (TextUtils.equals(packageName,
- enabledNotificationListeners.get(i).getPackageName())) {
- return true;
- }
- }
- }
+ return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
+ controllerPid, controllerUid);
} finally {
Binder.restoreCallingIdentity(token);
}
- if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled notification"
- + " listener");
- }
- return false;
}
/**
@@ -1614,60 +1582,85 @@
destroySession2Internal(token);
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
+ // TODO(jaewan): Make this API take userId as an argument (b/73597722)
@Override
public List<Bundle> getSessionTokens(boolean activeSessionOnly,
- boolean sessionServiceOnly) throws RemoteException {
+ boolean sessionServiceOnly, String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+
List<Bundle> tokens = new ArrayList<>();
- synchronized (mLock) {
- for (Map.Entry<SessionToken2, MediaController2> record
- : mSessionRecords.entrySet()) {
- boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
- boolean isActive = record.getValue() != null;
- if ((activeSessionOnly && !isActive)
- || (sessionServiceOnly && !isSessionService) ){
- continue;
+ try {
+ verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+ synchronized (mLock) {
+ for (Map.Entry<SessionToken2, MediaController2> record
+ : mSessionRecords.entrySet()) {
+ boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
+ boolean isActive = record.getValue() != null;
+ if ((activeSessionOnly && !isActive)
+ || (sessionServiceOnly && !isSessionService)) {
+ continue;
+ }
+ tokens.add(record.getKey().toBundle());
}
- tokens.add(record.getKey().toBundle());
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return tokens;
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
- // TODO(jaewan): "userId != calling user" needs extra protection (b/73226436)
@Override
public void addSessionTokensListener(ISessionTokensListener listener, int userId,
- String packageName) {
- synchronized (mLock) {
- final SessionTokensListenerRecord record =
- new SessionTokensListenerRecord(listener, userId);
- try {
- listener.asBinder().linkToDeath(record, 0);
- } catch (RemoteException e) {
+ String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
+ synchronized (mLock) {
+ final SessionTokensListenerRecord record =
+ new SessionTokensListenerRecord(listener, resolvedUserId);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ }
+ mSessionTokensListeners.add(record);
}
- mSessionTokensListeners.add(record);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
- // TODO(jaewan): Protect this API with permission (b/73226436)
+ // TODO(jaewan): Make this API take userId as an argument (b/73597722)
@Override
- public void removeSessionTokensListener(ISessionTokensListener listener) {
- synchronized (mLock) {
- IBinder listenerBinder = listener.asBinder();
- for (SessionTokensListenerRecord record : mSessionTokensListeners) {
- if (listenerBinder.equals(record.mListener.asBinder())) {
- try {
- listenerBinder.unlinkToDeath(record, 0);
- } catch (NoSuchElementException e) {
+ public void removeSessionTokensListener(ISessionTokensListener listener,
+ String packageName) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+ synchronized (mLock) {
+ IBinder listenerBinder = listener.asBinder();
+ for (SessionTokensListenerRecord record : mSessionTokensListeners) {
+ if (listenerBinder.equals(record.mListener.asBinder())) {
+ try {
+ listenerBinder.unlinkToDeath(record, 0);
+ } catch (NoSuchElementException e) {
+ }
+ mSessionTokensListeners.remove(record);
+ break;
}
- mSessionTokensListeners.remove(record);
- break;
}
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
+ // For MediaSession
private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
final int uid) {
String packageName = null;
@@ -1687,6 +1680,66 @@
return resolvedUserId;
}
+ // For MediaSession2
+ private int verifySessionsRequest2(int targetUserId, String callerPackageName,
+ int callerPid, int callerUid) throws RemoteException {
+ // Check that they can make calls on behalf of the user and get the final user id.
+ int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
+ targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
+ callerPackageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ if (!hasMediaControlPermission(
+ resolvedUserId, callerPackageName, callerPid, callerUid)) {
+ throw new SecurityException("Missing permission to control media.");
+ }
+ return resolvedUserId;
+ }
+
+ // For MediaSession2
+ private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
+ int pid, int uid) throws RemoteException {
+ // Allow API calls from the System UI
+ if (isCurrentVolumeController(pid, uid)) {
+ return true;
+ }
+
+ // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+ // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+ // check here.
+ if (uid == Process.SYSTEM_UID || getContext().checkPermission(
+ android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ } else if (DEBUG) {
+ Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+ }
+
+ // You may not access another user's content as an enabled listener.
+ final int userId = UserHandle.getUserId(uid);
+ if (resolvedUserId != userId) {
+ return false;
+ }
+
+ // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
+ // String pkgName) to notification team for optimization
+ final List<ComponentName> enabledNotificationListeners =
+ mNotificationManager.getEnabledNotificationListeners(userId);
+ if (enabledNotificationListeners != null) {
+ for (int i = 0; i < enabledNotificationListeners.size(); i++) {
+ if (TextUtils.equals(packageName,
+ enabledNotificationListeners.get(i).getPackageName())) {
+ return true;
+ }
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+ + "notification listener");
+ }
+ return false;
+ }
+
private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 052f239..576d228 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23,7 +23,6 @@
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -98,7 +97,6 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
@@ -214,6 +212,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PatternMatcher;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -238,7 +237,6 @@
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.service.pm.PackageServiceDumpProto;
-import android.service.textclassifier.TextClassifierService;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -407,7 +405,7 @@
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
public static final boolean DEBUG_INSTALL = false;
- public static final boolean DEBUG_REMOVE = false;
+ public static final boolean DEBUG_REMOVE = true;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -10355,7 +10353,7 @@
if (Build.IS_DEBUGGABLE &&
pkg.isPrivileged() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ !SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, true)) {
PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
}
@@ -13964,29 +13962,45 @@
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, String callingPackage,
int userId) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ try {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null);
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_USERS,
+ "Callers need to have either " + Manifest.permission.SUSPEND_APPS + " or "
+ + Manifest.permission.MANAGE_USERS);
+ }
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setPackagesSuspended for user " + userId);
+ if (callingUid != Process.ROOT_UID &&
+ !UserHandle.isSameApp(getPackageUid(callingPackage, 0, userId), callingUid)) {
+ throw new IllegalArgumentException("callingPackage " + callingPackage + " does not"
+ + " belong to calling app id " + UserHandle.getAppId(callingUid));
+ }
if (ArrayUtils.isEmpty(packageNames)) {
return packageNames;
}
// List of package names for whom the suspended state has changed.
- List<String> changedPackages = new ArrayList<>(packageNames.length);
+ final List<String> changedPackages = new ArrayList<>(packageNames.length);
// List of package names for whom the suspended state is not set as requested in this
// method.
- List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- long callingId = Binder.clearCallingIdentity();
+ final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+ final long callingId = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < packageNames.length; i++) {
- String packageName = packageNames[i];
- boolean changed = false;
- final int appId;
- synchronized (mPackages) {
+ synchronized (mPackages) {
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ if (packageName == callingPackage) {
+ Slog.w(TAG, "Calling package: " + callingPackage + "trying to "
+ + (suspended ? "" : "un") + "suspend itself. Ignoring");
+ unactionedPackages.add(packageName);
+ continue;
+ }
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null
|| filterAppAccessLPr(pkgSetting, callingUid, userId)) {
@@ -13995,42 +14009,75 @@
unactionedPackages.add(packageName);
continue;
}
- appId = pkgSetting.appId;
if (pkgSetting.getSuspended(userId) != suspended) {
if (!canSuspendPackageForUserLocked(packageName, userId)) {
unactionedPackages.add(packageName);
continue;
}
- pkgSetting.setSuspended(suspended, userId);
- mSettings.writePackageRestrictionsLPr(userId);
- changed = true;
+ pkgSetting.setSuspended(suspended, callingPackage, appExtras,
+ launcherExtras, userId);
changedPackages.add(packageName);
}
}
-
- if (changed && suspended) {
- killApplication(packageName, UserHandle.getUid(userId, appId),
- "suspending package");
- }
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
-
+ // TODO (b/75036698): Also send each package a broadcast when suspended state changed
if (!changedPackages.isEmpty()) {
sendPackagesSuspendedForUser(changedPackages.toArray(
new String[changedPackages.size()]), userId, suspended);
+ synchronized (mPackages) {
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
}
return unactionedPackages.toArray(new String[unactionedPackages.size()]);
}
@Override
+ public PersistableBundle getPackageSuspendedAppExtras(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (getPackageUid(packageName, 0, userId) != callingUid) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+ }
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown target package: " + packageName);
+ }
+ final PackageUserState packageUserState = ps.readUserState(userId);
+ return packageUserState.suspended ? packageUserState.suspendedAppExtras : null;
+ }
+ }
+
+ @Override
+ public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown target package: " + packageName);
+ }
+ final PackageUserState packageUserState = ps.readUserState(userId);
+ if (packageUserState.suspended) {
+ // TODO (b/75036698): Also send this package a broadcast with the new app extras
+ packageUserState.suspendedAppExtras = appExtras;
+ }
+ }
+ }
+
+ @Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isPackageSuspendedForUser for user " + userId);
+ if (getPackageUid(packageName, 0, userId) != callingUid) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+ }
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
@@ -14040,6 +14087,21 @@
}
}
+ void onSuspendingPackageRemoved(String packageName, int userId) {
+ final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+ : new int[] {userId};
+ synchronized (mPackages) {
+ for (PackageSetting ps : mSettings.mPackages.values()) {
+ for (int user : userIds) {
+ final PackageUserState pus = ps.readUserState(user);
+ if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
+ ps.setSuspended(false, null, null, null, user);
+ }
+ }
+ }
+ }
+ }
+
@GuardedBy("mPackages")
private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
if (isPackageDeviceAdmin(packageName, userId)) {
@@ -14096,6 +14158,11 @@
return false;
}
+ if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName);
+ return false;
+ }
+
return true;
}
@@ -18612,6 +18679,7 @@
}
final int removedUserId = (user != null) ? user.getIdentifier()
: UserHandle.USER_ALL;
+
if (!clearPackageStateForUserLIF(ps, removedUserId, outInfo)) {
return false;
}
@@ -18620,6 +18688,11 @@
return true;
}
}
+ if (ps.getPermissionsState().hasPermission(
+ Manifest.permission.SUSPEND_APPS, user.getIdentifier())) {
+ onSuspendingPackageRemoved(packageName, user.getIdentifier());
+ }
+
if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
&& user.getIdentifier() != UserHandle.USER_ALL)) {
@@ -18758,6 +18831,9 @@
true /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
+ null, /*suspendingPackage*/
+ null, /*suspendedAppExtras*/
+ null, /*suspendedLauncherExtras*/
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d2ef67b..28e32a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -57,12 +57,14 @@
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -1503,27 +1505,55 @@
private int runSuspend(boolean suspendedState) {
final PrintWriter pw = getOutPrintWriter();
int userId = UserHandle.USER_SYSTEM;
+ final PersistableBundle appExtras = new PersistableBundle();
+ final PersistableBundle launcherExtras = new PersistableBundle();
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
+ case "--ael":
+ case "--aes":
+ case "--aed":
+ case "--lel":
+ case "--les":
+ case "--led":
+ final String key = getNextArgRequired();
+ final String val = getNextArgRequired();
+ if (!suspendedState) {
+ break;
+ }
+ final PersistableBundle bundleToInsert =
+ opt.startsWith("--a") ? appExtras : launcherExtras;
+ switch (opt.charAt(4)) {
+ case 'l':
+ bundleToInsert.putLong(key, Long.valueOf(val));
+ break;
+ case 'd':
+ bundleToInsert.putDouble(key, Double.valueOf(val));
+ break;
+ case 's':
+ bundleToInsert.putString(key, val);
+ break;
+ }
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
}
}
- String packageName = getNextArg();
+ final String packageName = getNextArg();
if (packageName == null) {
pw.println("Error: package name not specified");
return 1;
}
-
+ final String callingPackage =
+ (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
try {
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
- userId);
+ appExtras, launcherExtras, callingPackage, userId);
pw.println("Package " + packageName + " new suspended state: "
+ mInterface.isPackageSuspendedForUser(packageName, userId));
return 0;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index a0ed126..008a81c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,12 +20,16 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -394,8 +398,13 @@
return readUserState(userId).suspended;
}
- void setSuspended(boolean suspended, int userId) {
- modifyUserState(userId).suspended = suspended;
+ void setSuspended(boolean suspended, String suspendingPackage, PersistableBundle appExtras,
+ PersistableBundle launcherExtras, int userId) {
+ final PackageUserState existingUserState = modifyUserState(userId);
+ existingUserState.suspended = suspended;
+ existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
+ existingUserState.suspendedAppExtras = suspended ? appExtras : null;
+ existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
}
public boolean getInstantApp(int userId) {
@@ -415,7 +424,9 @@
}
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
+ boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
+ PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras,
+ boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int domainVerifState, int linkGeneration, int installReason,
@@ -428,6 +439,9 @@
state.notLaunched = notLaunched;
state.hidden = hidden;
state.suspended = suspended;
+ state.suspendingPackage = suspendingPackage;
+ state.suspendedAppExtras = suspendedAppExtras;
+ state.suspendedLauncherExtras = suspendedLauncherExtras;
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
@@ -594,6 +608,9 @@
proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
+ if (state.suspended) {
+ proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
+ }
proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a38cbda..d0e8544 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -31,6 +31,7 @@
import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -58,6 +59,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.PatternMatcher;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -199,6 +201,8 @@
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
+ private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
+ private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
public static final String ATTR_NAME = "name";
public static final String ATTR_PACKAGE = "package";
@@ -217,6 +221,7 @@
// New name for the above attribute.
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_SUSPENDED = "suspended";
+ private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
// Legacy, uninstall blocks are stored separately.
@Deprecated
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -728,6 +733,9 @@
true /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
+ null, /*suspendingPackage*/
+ null, /*suspendedAppExtras*/
+ null, /*suspendedLauncherExtras*/
instantApp,
virtualPreload,
null /*lastDisableAppCaller*/,
@@ -1619,6 +1627,9 @@
false /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
+ null, /*suspendingPackage*/
+ null, /*suspendedAppExtras*/
+ null, /*suspendedLauncherExtras*/
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
@@ -1691,6 +1702,12 @@
final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
false);
+ String suspendingPackage = parser.getAttributeValue(null,
+ ATTR_SUSPENDING_PACKAGE);
+ if (suspended && suspendingPackage == null) {
+ suspendingPackage = PLATFORM_PACKAGE_NAME;
+ }
+
final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser,
ATTR_BLOCK_UNINSTALL, false);
final boolean instantApp = XmlUtils.readBooleanAttribute(parser,
@@ -1716,6 +1733,8 @@
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
+ PersistableBundle suspendedAppExtras = null;
+ PersistableBundle suspendedLauncherExtras = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1725,11 +1744,22 @@
|| type == XmlPullParser.TEXT) {
continue;
}
- tagName = parser.getName();
- if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
- enabledComponents = readComponentsLPr(parser);
- } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
- disabledComponents = readComponentsLPr(parser);
+ switch (parser.getName()) {
+ case TAG_ENABLED_COMPONENTS:
+ enabledComponents = readComponentsLPr(parser);
+ break;
+ case TAG_DISABLED_COMPONENTS:
+ disabledComponents = readComponentsLPr(parser);
+ break;
+ case TAG_SUSPENDED_APP_EXTRAS:
+ suspendedAppExtras = PersistableBundle.restoreFromXml(parser);
+ break;
+ case TAG_SUSPENDED_LAUNCHER_EXTRAS:
+ suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
+ + TAG_PACKAGE);
}
}
@@ -1737,7 +1767,8 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, suspended, instantApp, virtualPreload, enabledCaller,
+ hidden, suspended, suspendingPackage, suspendedAppExtras,
+ suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
enabledComponents, disabledComponents, verifState, linkGeneration,
installReason, harmfulAppWarning);
} else if (tagName.equals("preferred-activities")) {
@@ -2046,6 +2077,27 @@
}
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
+ serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, ustate.suspendingPackage);
+ if (ustate.suspendedAppExtras != null) {
+ serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
+ try {
+ ustate.suspendedAppExtras.saveToXml(serializer);
+ } catch (XmlPullParserException xmle) {
+ Slog.wtf(TAG, "Exception while trying to write suspendedAppExtras for "
+ + pkg + ". Will be lost on reboot", xmle);
+ }
+ serializer.endTag(null, TAG_SUSPENDED_APP_EXTRAS);
+ }
+ if (ustate.suspendedLauncherExtras != null) {
+ serializer.startTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
+ try {
+ ustate.suspendedLauncherExtras.saveToXml(serializer);
+ } catch (XmlPullParserException xmle) {
+ Slog.wtf(TAG, "Exception while trying to write suspendedLauncherExtras"
+ + " for " + pkg + ". Will be lost on reboot", xmle);
+ }
+ serializer.endTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
+ }
}
if (ustate.instantApp) {
serializer.attribute(null, ATTR_INSTANT_APP, "true");
@@ -4697,6 +4749,10 @@
pw.print(ps.getHidden(user.id));
pw.print(" suspended=");
pw.print(ps.getSuspended(user.id));
+ if (ps.getSuspended(user.id)) {
+ pw.print(" suspendingPackage=");
+ pw.print(ps.readUserState(user.id).suspendingPackage);
+ }
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
pw.print(" notLaunched=");
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 062a6b8..41ed6f2 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -198,7 +198,14 @@
ParcelFileDescriptor fd = null;
try {
fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
- postSuccess(packageName, fd, callback);
+ if (fd == null || !fd.getFileDescriptor().valid()) {
+ Slog.wtf(TAG,
+ "ParcelFileDescriptor.open returned an invalid descriptor for "
+ + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ } else {
+ postSuccess(packageName, fd, callback);
+ }
} catch (FileNotFoundException e) {
Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
+ snapshotProfile, e);
@@ -264,7 +271,7 @@
mHandler.post(() -> {
try {
callback.onError(errCode);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
}
});
@@ -277,8 +284,17 @@
}
mHandler.post(() -> {
try {
- callback.onSuccess(fd);
- } catch (RemoteException e) {
+ // Double check that the descriptor is still valid.
+ // We've seen production issues (b/76028139) where this can turn invalid (there are
+ // suspicions around the finalizer behaviour).
+ if (fd.getFileDescriptor().valid()) {
+ callback.onSuccess(fd);
+ } else {
+ Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for "
+ + packageName);
+ callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ }
+ } catch (Exception e) {
Slog.w(TAG,
"Failed to call onSuccess after profile snapshot for " + packageName, e);
} finally {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 83fe1c9..ad32ed3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -264,13 +264,9 @@
}
public void grantDefaultPermissions(int userId) {
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- grantAllRuntimePermissions(userId);
- } else {
- grantPermissionsToSysComponentsAndPrivApps(userId);
- grantDefaultSystemHandlerPermissions(userId);
- grantDefaultPermissionExceptions(userId);
- }
+ grantPermissionsToSysComponentsAndPrivApps(userId);
+ grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
}
private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
@@ -1247,6 +1243,13 @@
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
+ // For IoT devices, we check the oem partition for default permissions for each app.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+ dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ }
return ret.isEmpty() ? null : ret.toArray(new File[0]);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6b70f5c..4dc68ac 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -39,6 +39,7 @@
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
+import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.STATE_OFF;
import static android.view.WindowManager.DOCKED_LEFT;
@@ -71,6 +72,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -187,6 +189,7 @@
import android.hardware.power.V1_0.PowerHint;
import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.session.MediaSessionLegacyHelper;
@@ -455,6 +458,7 @@
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
StatusBarManagerInternal mStatusBarManagerInternal;
+ AudioManagerInternal mAudioManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -772,6 +776,9 @@
private boolean mScreenshotChordPowerKeyTriggered;
private long mScreenshotChordPowerKeyTime;
+ // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
+ private int mRingerToggleChord = VOLUME_HUSH_OFF;
+
private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000;
private boolean mBugreportTvKey1Pressed;
@@ -836,6 +843,7 @@
private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27;
private static final int MSG_POWER_VERY_LONG_PRESS = 28;
private static final int MSG_NOTIFY_USER_ACTIVITY = 29;
+ private static final int MSG_RINGER_TOGGLE_CHORD = 30;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -943,6 +951,8 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.USER_ACTIVITY);
+ case MSG_RINGER_TOGGLE_CHORD:
+ handleRingerChordGesture();
break;
}
}
@@ -996,6 +1006,9 @@
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
+ UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POLICY_CONTROL), false, this,
UserHandle.USER_ALL);
@@ -1117,6 +1130,14 @@
@VisibleForTesting
SystemGesturesPointerEventListener mSystemGestures;
+ private void handleRingerChordGesture() {
+ if (mRingerToggleChord == VOLUME_HUSH_OFF) {
+ return;
+ }
+ getAudioManagerInternal();
+ mAudioManagerInternal.silenceRingerModeInternal("volume_hush");
+ }
+
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
if (mStatusBarService == null) {
@@ -1137,6 +1158,15 @@
}
}
+ AudioManagerInternal getAudioManagerInternal() {
+ synchronized (mServiceAquireLock) {
+ if (mAudioManagerInternal == null) {
+ mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+ }
+ return mAudioManagerInternal;
+ }
+ }
+
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
@@ -1306,6 +1336,7 @@
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
+ interceptRingerToggleChord();
}
// Stop ringing or end call if configured to do so when power is pressed.
@@ -1701,6 +1732,22 @@
}
}
+ private void interceptRingerToggleChord() {
+ if (mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF
+ && mScreenshotChordPowerKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered) {
+ final long now = SystemClock.uptimeMillis();
+ if (now <= mA11yShortcutChordVolumeUpKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
+ && now <= mScreenshotChordPowerKeyTime
+ + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
+ mA11yShortcutChordVolumeUpKeyConsumed = true;
+ cancelPendingPowerKeyAction();
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
+ getRingerToggleChordDelay());
+ }
+ }
+ }
+
private long getAccessibilityShortcutTimeout() {
ViewConfiguration config = ViewConfiguration.get(mContext);
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -1718,6 +1765,11 @@
return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
}
+ private long getRingerToggleChordDelay() {
+ // Always timeout like a tap
+ return ViewConfiguration.getTapTimeout();
+ }
+
private void cancelPendingScreenshotChordAction() {
mHandler.removeCallbacks(mScreenshotRunnable);
}
@@ -1726,6 +1778,10 @@
mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
}
+ private void cancelPendingRingerToggleChordAction() {
+ mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
+ }
+
private final Runnable mEndCallLongPress = new Runnable() {
@Override
public void run() {
@@ -2382,7 +2438,13 @@
mSystemNavigationKeysEnabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
0, UserHandle.USER_CURRENT) == 1;
-
+ mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
+ UserHandle.USER_CURRENT);
+ if (!mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
+ mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
+ }
// Configure rotation suggestions.
int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
@@ -2522,7 +2584,14 @@
/** {@inheritDoc} */
@Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
- int type = attrs.type;
+ final int type = attrs.type;
+ final boolean isRoundedCornerOverlay =
+ (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+
+ if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
+ != PERMISSION_GRANTED) {
+ return ADD_PERMISSION_DENIED;
+ }
outAppOp[0] = AppOpsManager.OP_NONE;
@@ -3531,6 +3600,25 @@
}
}
+ // If a ringer toggle chord could be on the way but we're not sure, then tell the dispatcher
+ // to wait a little while and try again later before dispatching.
+ if (mRingerToggleChord != VOLUME_HUSH_OFF && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
+ if (mA11yShortcutChordVolumeUpKeyTriggered && !mScreenshotChordPowerKeyTriggered) {
+ final long now = SystemClock.uptimeMillis();
+ final long timeoutTime = mA11yShortcutChordVolumeUpKeyTime
+ + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
+ if (now < timeoutTime) {
+ return timeoutTime - now;
+ }
+ }
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
+ if (!down) {
+ mA11yShortcutChordVolumeUpKeyConsumed = false;
+ }
+ return -1;
+ }
+ }
+
// Cancel any pending meta actions if we see any other keys being pressed between the down
// of the meta key and its corresponding up.
if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
@@ -5997,6 +6085,9 @@
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
+ // Any activity on the vol down button stops the ringer toggle shortcut
+ cancelPendingRingerToggleChordAction();
+
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordVolumeDownKeyTriggered = true;
@@ -6020,12 +6111,16 @@
mA11yShortcutChordVolumeUpKeyConsumed = false;
cancelPendingPowerKeyAction();
cancelPendingScreenshotChordAction();
+ cancelPendingRingerToggleChordAction();
+
interceptAccessibilityShortcutChord();
+ interceptRingerToggleChord();
}
} else {
mA11yShortcutChordVolumeUpKeyTriggered = false;
cancelPendingScreenshotChordAction();
cancelPendingAccessibilityShortcutAction();
+ cancelPendingRingerToggleChordAction();
}
}
if (down) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a76857e..85436da 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1698,7 +1698,10 @@
stack.getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
}
- if (mService.mAppTransition.getRemoteAnimationController() != null) {
+
+ // Delaying animation start isn't compatible with remote animations at all.
+ if (mService.mAppTransition.getRemoteAnimationController() != null
+ && !mSurfaceAnimator.isAnimationStartDelayed()) {
adapter = mService.mAppTransition.getRemoteAnimationController()
.createAnimationAdapter(this, mTmpPoint, mTmpRect);
} else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2094755..3e47ea6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3807,9 +3807,15 @@
// we use relative layering of the IME targets child windows, and place the
// IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
//
+ // In the case the IME target is animating, the animation Z order may be different
+ // than the WindowContainer Z order, so it's difficult to be sure we have the correct
+ // IME target. In this case we just layer the IME over all transitions by placing it in the
+ // above applications layer.
+ //
// In the case where we have no IME target we assign it where it's base layer would
// place it in the AboveAppWindowContainers.
- if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode()
+ if (imeTarget != null && !(imeTarget.inSplitScreenWindowingMode()
+ || imeTarget.mToken.isAppAnimating())
&& (imeTarget.getSurfaceControl() != null)) {
mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d7f480b..c590067 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -99,6 +99,10 @@
mFinishedCallback = new FinishedCallback(this);
final RemoteAnimationTarget[] animations = createAnimations();
+ if (animations.length == 0) {
+ onAnimationFinished();
+ return;
+ }
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
@@ -132,6 +136,8 @@
mPendingAnimations.get(i).createRemoteAppAnimation();
if (target != null) {
targets.add(target);
+ } else {
+ mPendingAnimations.remove(i);
}
}
return targets.toArray(new RemoteAnimationTarget[targets.size()]);
@@ -225,10 +231,8 @@
RemoteAnimationTarget createRemoteAppAnimation() {
final Task task = mAppWindowToken.getTask();
final WindowState mainWindow = mAppWindowToken.findMainWindow();
- if (task == null) {
- return null;
- }
- if (mainWindow == null) {
+ if (task == null || mainWindow == null || mCapturedFinishCallback == null
+ || mCapturedLeash == null) {
return null;
}
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index e5928b1..ba3d091 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -234,6 +234,10 @@
mService.mAnimationTransferMap.put(mAnimation, this);
}
+ boolean isAnimationStartDelayed() {
+ return mAnimationStartDelayed;
+ }
+
/**
* Cancels the animation, and resets the leash.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a22bb00..56b314f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -48,6 +48,7 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -1214,8 +1215,10 @@
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+ final boolean isRoundedCornerOverlay =
+ (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
- session.mCanAddInternalSystemWindow);
+ session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
if (atoken == null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f727296..14680d9 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -53,6 +54,9 @@
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
+ /** {@code true} if this holds the rounded corner overlay */
+ final boolean mRoundedCornerOverlay;
+
// Set if this token was explicitly added by a client, so should
// persist (not be removed) when all windows are removed.
boolean mPersistOnEmpty;
@@ -105,11 +109,18 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens) {
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ false /* roundedCornersOverlay */);
+ }
+
+ WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
+ DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+ mRoundedCornerOverlay = roundedCornerOverlay;
onDisplayChanged(dc);
}
@@ -259,6 +270,12 @@
dc.reParentWindowToken(this);
mDisplayContent = dc;
+ // The rounded corner overlay should not be rotated. We ensure that by moving it outside
+ // the windowing layer.
+ if (mRoundedCornerOverlay) {
+ mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
+ }
+
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
// to another display before the window behind
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index cf42c0c..dcee151 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -125,6 +125,9 @@
}
};
+// Must match the value from GnssMeasurement.java
+static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss_V1_0> gnssHal = nullptr;
sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
@@ -807,7 +810,8 @@
SET(PseudorangeRateUncertaintyMetersPerSecond,
measurement->pseudorangeRateUncertaintyMps);
SET(AccumulatedDeltaRangeState,
- (static_cast<int32_t>(measurement->accumulatedDeltaRangeState)));
+ (static_cast<int32_t>(measurement->accumulatedDeltaRangeState) &
+ !ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0
SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM);
SET(AccumulatedDeltaRangeUncertaintyMeters,
measurement->accumulatedDeltaRangeUncertaintyM);
@@ -888,9 +892,10 @@
if (measurements_v1_1 != NULL) {
translateGnssMeasurement_V1_0(env, &(measurements_v1_1[i].v1_0), object);
- // Set the V1_1 flag
+ // Set the V1_1 flag, and mark that new field has valid information for Java Layer
SET(AccumulatedDeltaRangeState,
- static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState));
+ (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
+ ADR_STATE_HALF_CYCLE_REPORTED));
} else {
translateGnssMeasurement_V1_0(env, &(measurements_v1_0[i]), object);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 884f348..2e07703 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9252,7 +9252,7 @@
long id = mInjector.binderClearCallingIdentity();
try {
return mIPackageManager.setPackagesSuspendedAsUser(
- packageNames, suspended, callingUserId);
+ packageNames, suspended, null, null, "android", callingUserId);
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed talking to the package manager", re);
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 0ca0a1a..cdb339a 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -36,6 +36,7 @@
LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
+LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
LOCAL_JAVA_LIBRARIES := \
android.hidl.manager-V1.0-java \
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 372b8cc..ce98d65 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -62,6 +62,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.SUSPEND_APPS"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 23e5072..082827c 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -15,9 +15,11 @@
-->
<configuration description="Runs Frameworks Services Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksServicesTests.apk" />
<option name="test-file-name" value="JobTestApp.apk" />
<option name="test-file-name" value="ConnTestApp.apk" />
+ <option name="test-file-name" value="SuspendTestApp.apk" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
diff --git a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
index c348e70..d5a28f6 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
@@ -27,8 +27,6 @@
@RunWith(JUnit4.class)
public class AutofillManagerServiceTest {
- // TODO(b/74445943): temporary work around until P Development Preview 3 is branched
- private static final boolean ADDS_DEFAULT_BUTTON = true;
@Test
public void testGetWhitelistedCompatModePackages_null() {
@@ -42,16 +40,8 @@
@Test
public void testGetWhitelistedCompatModePackages_onePackageNoUrls() {
- if (ADDS_DEFAULT_BUTTON) {
- final Map<String, String[]> result =
- getWhitelistedCompatModePackages("one_is_the_loniest_package");
- assertThat(result).hasSize(1);
- assertThat(result.get("one_is_the_loniest_package")).asList()
- .containsExactly("url_bar", "location_bar_edit_text");
- } else {
- assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
- .containsExactly("one_is_the_loniest_package", null);
- }
+ assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
+ .containsExactly("one_is_the_loniest_package", null);
}
@Test
@@ -80,12 +70,7 @@
public void testGetWhitelistedCompatModePackages_multiplePackagesOneInvalid() {
final Map<String, String[]> result = getWhitelistedCompatModePackages("one:two[");
assertThat(result).hasSize(1);
- if (ADDS_DEFAULT_BUTTON) {
- assertThat(result.get("one")).asList()
- .containsExactly("url_bar", "location_bar_edit_text");
- } else {
- assertThat(result.get("one")).isNull();
- }
+ assertThat(result.get("one")).isNull();
}
@Test
@@ -94,12 +79,7 @@
getWhitelistedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
assertThat(result).hasSize(3);
assertThat(result.get("p1")).asList().containsExactly("p1u1");
- if (ADDS_DEFAULT_BUTTON) {
- assertThat(result.get("p2")).asList()
- .containsExactly("url_bar", "location_bar_edit_text");
- } else {
- assertThat(result.get("p2")).isNull();
- }
+ assertThat(result.get("p2")).isNull();
assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 10a21fd..2d5afad 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -874,8 +875,8 @@
}
@Override
- public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
- int userId) {
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
return new String[0];
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
index 9213268..e3e61ac 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
@@ -16,9 +16,8 @@
package com.android.server.devicepolicy;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-
-
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
.OWNER_TRANSFER_METADATA_XML;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.TAG_ADMIN_TYPE;
@@ -32,6 +31,7 @@
import android.os.Environment;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Injector;
import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Metadata;
@@ -57,6 +57,7 @@
@RunWith(AndroidJUnit4.class)
public class TransferOwnershipMetadataManagerTest {
+ private final static String TAG = TransferOwnershipMetadataManagerTest.class.getName();
private final static String SOURCE_COMPONENT =
"com.dummy.admin.package/com.dummy.admin.package.SourceClassName";
private final static String TARGET_COMPONENT =
@@ -106,6 +107,22 @@
@Test
public void testLoad() {
TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ final File transferOwnershipMetadataFile =
+ new File(mMockInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML);
+ Log.d(TAG, "testLoad: file path is " + transferOwnershipMetadataFile.getAbsolutePath());
+ Log.d(TAG, "testLoad: file exists? " + transferOwnershipMetadataFile.exists());
+ Log.d(TAG, "testLoad: file mkdir?" + transferOwnershipMetadataFile.mkdir());
+ try {
+ File canonicalFile = transferOwnershipMetadataFile.getCanonicalFile();
+ File parentFile = canonicalFile.getParentFile();
+ Log.d(TAG, "testLoad: file getCanonicalFile?" + canonicalFile);
+ Log.d(TAG, "testLoad: getCanonicalFile.getParentFile " + parentFile);
+ Log.d(TAG, "testLoad: parent mkdirs? " + parentFile.mkdirs());
+ Log.d(TAG, "testLoad: parent exists? " + parentFile.exists());
+ Log.d(TAG, "testLoad: canonical file.mkdir()? " + canonicalFile.mkdir());
+ } catch (IOException e) {
+ Log.d(TAG, "testLoad: failed to get canonical file");
+ }
paramsManager.saveMetadataFile(TEST_PARAMS);
assertEquals(TEST_PARAMS, paramsManager.loadMetadataFile());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 25747b8..0ea2317 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -62,7 +62,6 @@
import java.io.File;
import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -95,7 +94,6 @@
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
- private KeyPair mKeyPair;
private AndroidKeyStoreSecretKey mWrappingKey;
private PlatformEncryptionKey mEncryptKey;
@@ -108,7 +106,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- mKeyPair = SecureBox.genKeyPair();
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
new int[] {TYPE_LOCKSCREEN});
@@ -249,8 +246,8 @@
TEST_RECOVERY_AGENT_UID,
TEST_APP_KEY_ALIAS,
WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
mKeySyncTask.run();
@@ -265,8 +262,8 @@
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
mKeySyncTask.run();
@@ -275,8 +272,8 @@
@Test
public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setServerParams(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
@@ -301,7 +298,7 @@
lockScreenHash,
keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
- mKeyPair.getPublic(),
+ TestData.CERT_1_PUBLIC_KEY,
counterId,
/*maxAttempts=*/ 10,
TEST_VAULT_HANDLE));
@@ -309,8 +306,8 @@
assertThat(applicationKeys).hasSize(1);
assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
- assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
- .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic()));
+ assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+ .isEqualTo(TestData.CERT_PATH_1);
assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
WrappedApplicationKey keyData = applicationKeys.get(0);
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
@@ -335,15 +332,14 @@
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
assertThat(applicationKeys).hasSize(1);
- assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
- .isEqualTo(SecureBox.encodePublicKey(
- TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
+ assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+ .isEqualTo(TestData.CERT_PATH_1);
}
@Test
public void run_setsCorrectSnapshotVersion() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -361,8 +357,8 @@
@Test
public void run_recreatesMissingSnapshot() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -391,8 +387,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -417,8 +413,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -444,8 +440,8 @@
/*credentialUpdated=*/ false,
mPlatformKeyManager);
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -460,10 +456,10 @@
@Test
public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -482,10 +478,10 @@
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
new int[] {1000});
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -499,10 +495,10 @@
@Test
public void run_notifiesNonregisteredAgent() throws Exception {
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -562,7 +558,7 @@
private byte[] decryptThmEncryptedKey(
byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
byte[] locallyEncryptedKey = SecureBox.decrypt(
- mKeyPair.getPrivate(),
+ TestData.CERT_1_PRIVATE_KEY,
/*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
/*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
encryptedKey
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index a251c9d..fae48c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -52,6 +52,8 @@
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
private static final byte[] TEST_VAULT_HANDLE =
new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+ private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
+ private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
private static final String SHA_256_ALGORITHM = "SHA-256";
private static final String APPLICATION_KEY_ALGORITHM = "AES";
private static final byte[] LOCK_SCREEN_HASH_1 =
@@ -63,8 +65,7 @@
private static final byte[] RECOVERY_RESPONSE_HEADER =
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
- private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
+
@Test
public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -345,7 +346,7 @@
}
@Test
- public void packVaultParams_returns94Bytes() throws Exception {
+ public void packVaultParams_returnsCorrectSize() throws Exception {
PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
byte[] packedForm = KeySyncUtils.packVaultParams(
@@ -420,6 +421,24 @@
assertArrayEquals(TEST_VAULT_HANDLE, vaultHandle);
}
+ @Test
+ public void packVaultParams_encodesVaultHandleWithLength8AsLastParam() throws Exception {
+ byte[] vaultHandleWithLenght8 = new byte[] {1, 2, 3, 4, 1, 2, 3, 4};
+ byte[] packedForm = KeySyncUtils.packVaultParams(
+ SecureBox.genKeyPair().getPublic(),
+ /*counterId=*/ 10021L,
+ /*maxAttempts=*/ 10,
+ vaultHandleWithLenght8);
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ assertEquals(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES + 8, packedForm.length);
+ byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
+ byte[] vaultHandle = new byte[8];
+ byteBuffer.get(vaultHandle);
+ assertArrayEquals(vaultHandleWithLenght8, vaultHandle);
+ }
+
private static byte[] randomBytes(int n) {
byte[] bytes = new byte[n];
new Random().nextBytes(bytes);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 260bb0a..0d6d525b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -242,8 +242,8 @@
try {
mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
fail("should have thrown");
- } catch (ServiceSpecificException e) {
- assertThat(e.getMessage()).contains("not contain 256 bits");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
}
}
@@ -300,6 +300,23 @@
}
@Test
+ public void initRecoveryService_regeneratesCounterId() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial + 1));
+ Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+
+ assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue();
+ }
+
+ @Test
public void initRecoveryService_throwsIfInvalidCert() throws Exception {
byte[] modifiedCertXml = TestData.getCertXml();
modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
@@ -393,7 +410,7 @@
ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
TestData.getSigXml());
fail("should have thrown");
- } catch (ServiceSpecificException e) {
+ } catch (NullPointerException e) {
assertThat(e.getMessage()).contains("is null");
}
}
@@ -405,7 +422,7 @@
ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
/*recoveryServiceSigFile=*/ null);
fail("should have thrown");
- } catch (ServiceSpecificException e) {
+ } catch (NullPointerException e) {
assertThat(e.getMessage()).contains("is null");
}
}
@@ -558,6 +575,16 @@
}
@Test
+ public void closeSession_throwsIfNullSession() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("invalid");
+ }
+ }
+
+ @Test
public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
try {
mRecoverableKeyStoreManager.startRecoverySession(
@@ -880,6 +907,16 @@
}
@Test
+ public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
+ @Test
public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -913,6 +950,16 @@
assertThat(statuses).containsEntry(alias, status2); // updated
}
+ @Test
+ public void setRecoveryStatus_throwsIfNullAlias() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100);
+ fail("should have thrown");
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage()).contains("is null");
+ }
+ }
+
private static byte[] encryptedApplicationKey(
SecretKey recoveryKey, byte[] applicationKey) throws Exception {
return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index b5d6ce8..4b059c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -1,11 +1,18 @@
package com.android.server.locksettings.recoverablekeystore;
+import static com.google.common.truth.Truth.assertThat;
+
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
-import java.security.cert.CertPath;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
+import java.security.spec.ECPrivateKeySpec;
public final class TestData {
@@ -213,6 +220,44 @@
+ " </value>\n"
+ "</signature>\n";
+ public static final PublicKey CERT_1_PUBLIC_KEY;
+ public static final PrivateKey CERT_1_PRIVATE_KEY;
+
+ static {
+ try {
+ CERT_1_PUBLIC_KEY =
+ SecureBox.decodePublicKey(
+ new byte[] {
+ (byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18,
+ (byte) 0x98, (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4,
+ (byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda, (byte) 0x1c,
+ (byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a,
+ (byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0,
+ (byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
+ (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
+ (byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79,
+ (byte) 0x20, (byte) 0x1d, (byte) 0x91, (byte) 0x55, (byte) 0xb0,
+ (byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b, (byte) 0x32,
+ (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc,
+ (byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
+ (byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa
+ });
+ CERT_1_PRIVATE_KEY =
+ decodePrivateKey(
+ new byte[] {
+ (byte) 0x70, (byte) 0x01, (byte) 0xc7, (byte) 0x87, (byte) 0x32,
+ (byte) 0x2f, (byte) 0x1c, (byte) 0x9a, (byte) 0x6e, (byte) 0xb1,
+ (byte) 0x91, (byte) 0xca, (byte) 0x4e, (byte) 0xb5, (byte) 0x44,
+ (byte) 0xba, (byte) 0xc8, (byte) 0x68, (byte) 0xc6, (byte) 0x0a,
+ (byte) 0x76, (byte) 0xcb, (byte) 0xd3, (byte) 0x63, (byte) 0x67,
+ (byte) 0x7c, (byte) 0xb0, (byte) 0x11, (byte) 0x82, (byte) 0x65,
+ (byte) 0x77, (byte) 0x01
+ });
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
public static byte[] getCertPath1Bytes() {
try {
return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
@@ -256,4 +301,11 @@
public static byte[] getSigXml() {
return THM_SIG_XML.getBytes(StandardCharsets.UTF_8);
}
+
+ private static PrivateKey decodePrivateKey(byte[] keyBytes) throws Exception {
+ assertThat(keyBytes.length).isEqualTo(32);
+ BigInteger priv = new BigInteger(/*signum=*/ 1, keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
deleted file mode 100644
index 180345c..0000000
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.locksettings.recoverablekeystore.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class PersistentKeyChainSnapshotTest {
-
- private static final String ALIAS = "some_key";
- private static final String ALIAS2 = "another_key";
- private static final byte[] RECOVERY_KEY_MATERIAL = "recovery_key_data"
- .getBytes(StandardCharsets.UTF_8);
- private static final byte[] KEY_MATERIAL = "app_key_data".getBytes(StandardCharsets.UTF_8);
- private static final byte[] PUBLIC_KEY = "public_key_data".getBytes(StandardCharsets.UTF_8);
- private static final byte[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
- private static final int SNAPSHOT_VERSION = 2;
- private static final int MAX_ATTEMPTS = 10;
- private static final long COUNTER_ID = 123456789L;
- private static final byte[] SERVER_PARAMS = "server_params".getBytes(StandardCharsets.UTF_8);
- private static final byte[] ZERO_BYTES = new byte[0];
- private static final byte[] ONE_BYTE = new byte[]{(byte) 11};
- private static final byte[] TWO_BYTES = new byte[]{(byte) 222,(byte) 222};
-
- @Test
- public void testWriteInt() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeInt(Integer.MIN_VALUE);
- writer.writeInt(Integer.MAX_VALUE);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readInt()).isEqualTo(Integer.MIN_VALUE);
- assertThat(reader.readInt()).isEqualTo(Integer.MAX_VALUE);
-
- assertThrows(
- IOException.class,
- () -> reader.readInt());
- }
-
- @Test
- public void testWriteLong() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeLong(Long.MIN_VALUE);
- writer.writeLong(Long.MAX_VALUE);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readLong()).isEqualTo(Long.MIN_VALUE);
- assertThat(reader.readLong()).isEqualTo(Long.MAX_VALUE);
-
- assertThrows(
- IOException.class,
- () -> reader.readLong());
- }
-
- @Test
- public void testWriteBytes() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeBytes(ZERO_BYTES);
- writer.writeBytes(ONE_BYTE);
- writer.writeBytes(TWO_BYTES);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readBytes()).isEqualTo(ZERO_BYTES);
- assertThat(reader.readBytes()).isEqualTo(ONE_BYTE);
- assertThat(reader.readBytes()).isEqualTo(TWO_BYTES);
-
- assertThrows(
- IOException.class,
- () -> reader.readBytes());
- }
-
- @Test
- public void testReadBytes_returnsNullArrayAsEmpty() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- writer.writeBytes(null);
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
- assertThat(reader.readBytes()).isEqualTo(new byte[]{}); // null -> empty array
- }
-
- @Test
- public void testWriteKeyEntry() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build();
- writer.writeKeyEntry(entry);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- WrappedApplicationKey copy = reader.readKeyEntry();
- assertThat(copy.getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyEntry());
- }
-
- @Test
- public void testWriteProtectionParams() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
- KeyChainProtectionParams protectionParams = new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build();
- writer.writeProtectionParams(protectionParams);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainProtectionParams copy = reader.readProtectionParams();
- assertThat(copy.getUserSecretType()).isEqualTo(1);
- assertThat(copy.getLockScreenUiFormat()).isEqualTo(2);
- assertThat(copy.getKeyDerivationParams().getSalt()).isEqualTo(SALT);
-
- assertThrows(
- IOException.class,
- () -> reader.readProtectionParams());
- }
-
- @Test
- public void testKeyChainSnapshot() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
-
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
- ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build());
-
- ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
-
- KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
- .setSnapshotVersion(SNAPSHOT_VERSION)
- .setKeyChainProtectionParams(protectionParamsList)
- .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
- .setWrappedApplicationKeys(appKeysList)
- .setMaxAttempts(MAX_ATTEMPTS)
- .setCounterId(COUNTER_ID)
- .setServerParams(SERVER_PARAMS)
- .setTrustedHardwarePublicKey(PUBLIC_KEY)
- .build();
-
- writer.writeKeyChainSnapshot(snapshot);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainSnapshot copy = reader.readKeyChainSnapshot();
- assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
- assertThat(copy.getKeyChainProtectionParams()).hasSize(1);
- assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
- assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
- assertThat(copy.getWrappedApplicationKeys()).hasSize(1);
- assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
- assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
- assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
- assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyChainSnapshot());
-
- verifyDeserialize(snapshot);
- }
-
- @Test
- public void testKeyChainSnapshot_withManyKeysAndProtectionParams() throws Exception {
- PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
- writer.initWriter();
-
- KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
- ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(1)
- .setLockScreenUiFormat(2)
- .setKeyDerivationParams(derivationParams)
- .build());
- protectionParamsList.add(new KeyChainProtectionParams.Builder()
- .setUserSecretType(2)
- .setLockScreenUiFormat(3)
- .setKeyDerivationParams(derivationParams)
- .build());
- ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
- appKeysList.add(new WrappedApplicationKey.Builder()
- .setAlias(ALIAS2)
- .setEncryptedKeyMaterial(KEY_MATERIAL)
- .build());
-
-
- KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
- .setSnapshotVersion(SNAPSHOT_VERSION)
- .setKeyChainProtectionParams(protectionParamsList)
- .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
- .setWrappedApplicationKeys(appKeysList)
- .setMaxAttempts(MAX_ATTEMPTS)
- .setCounterId(COUNTER_ID)
- .setServerParams(SERVER_PARAMS)
- .setTrustedHardwarePublicKey(PUBLIC_KEY)
- .build();
-
- writer.writeKeyChainSnapshot(snapshot);
-
- byte[] result = writer.getOutput();
-
- PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
- reader.initReader(result);
-
- KeyChainSnapshot copy = reader.readKeyChainSnapshot();
- assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
- assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
- assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
- assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
- assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
- assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
- assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
- assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
- assertThrows(
- IOException.class,
- () -> reader.readKeyChainSnapshot());
-
- verifyDeserialize(snapshot);
- }
-
- private void verifyDeserialize(KeyChainSnapshot snapshot) throws Exception {
- byte[] serialized = PersistentKeyChainSnapshot.serialize(snapshot);
- KeyChainSnapshot copy = PersistentKeyChainSnapshot.deserialize(serialized);
- assertThat(copy.getSnapshotVersion())
- .isEqualTo(snapshot.getSnapshotVersion());
- assertThat(copy.getKeyChainProtectionParams().size())
- .isEqualTo(copy.getKeyChainProtectionParams().size());
- assertThat(copy.getEncryptedRecoveryKeyBlob())
- .isEqualTo(snapshot.getEncryptedRecoveryKeyBlob());
- assertThat(copy.getWrappedApplicationKeys().size())
- .isEqualTo(snapshot.getWrappedApplicationKeys().size());
- assertThat(copy.getMaxAttempts()).isEqualTo(snapshot.getMaxAttempts());
- assertThat(copy.getCounterId()).isEqualTo(snapshot.getCounterId());
- assertThat(copy.getServerParams()).isEqualTo(snapshot.getServerParams());
- assertThat(copy.getTrustedHardwarePublicKey())
- .isEqualTo(snapshot.getTrustedHardwarePublicKey());
- }
-
- @Test
- public void testDeserialize_failsForNewerVersion() throws Exception {
- byte[] newVersion = new byte[]{(byte) 2, (byte) 0, (byte) 0, (byte) 0};
- assertThrows(
- IOException.class,
- () -> PersistentKeyChainSnapshot.deserialize(newVersion));
- }
-
- @Test
- public void testDeserialize_failsForEmptyData() throws Exception {
- byte[] empty = new byte[]{};
- assertThrows(
- IOException.class,
- () -> PersistentKeyChainSnapshot.deserialize(empty));
- }
-
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index 999dce5..6f2237f 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -75,11 +75,11 @@
Map<String, Boolean> result = PrivacyUtils.createDpEncodedReportMap(false, null,
TEST_DIGEST_LIST, TEST_AGGREGATED_RESULT1);
assertEquals(6, result.size());
- assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
+ assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
- assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
- assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+ assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+ assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 53d97e7..ebb4248 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -21,13 +21,12 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -39,12 +38,13 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.UserInfo;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.UserManagerInternal;
-import android.security.keystore.ArrayUtils;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -54,8 +54,8 @@
import com.android.server.LocalServices;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,9 +71,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerSettingsTests {
- private static final String PACKAGE_NAME_2 = "com.google.app2";
+ private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
- private static final String PACKAGE_NAME_1 = "com.google.app1";
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
public static final String TAG = "PackageManagerSettingsTests";
protected final String PREFIX = "android.content.pm";
@@ -156,6 +156,92 @@
assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT));
}
+ private PersistableBundle getPersistableBundle(String packageName, long longVal,
+ double doubleVal, boolean boolVal, String textVal) {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(packageName + ".TEXT_VALUE", textVal);
+ bundle.putLong(packageName + ".LONG_VALUE", longVal);
+ bundle.putBoolean(packageName + ".BOOL_VALUE", boolVal);
+ bundle.putDouble(packageName + ".DOUBLE_VALUE", doubleVal);
+ return bundle;
+ }
+
+ @Test
+ public void testReadPackageRestrictions_oldSuspendInfo() {
+ writePackageRestrictions_oldSuspendInfoXml(0);
+ final Object lock = new Object();
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+ settingsUnderTest.readPackageRestrictionsLPr(0);
+
+ final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
+ final PackageUserState packageUserState1 = ps1.readUserState(0);
+ assertThat(packageUserState1.suspended, is(true));
+ assertThat("android".equals(packageUserState1.suspendingPackage), is(true));
+
+ final PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
+ final PackageUserState packageUserState2 = ps2.readUserState(0);
+ assertThat(packageUserState2.suspended, is(false));
+ assertThat(packageUserState2.suspendingPackage, is(nullValue()));
+ }
+
+ @Test
+ public void testReadWritePackageRestrictions_newSuspendInfo() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+ final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
+
+ final PersistableBundle appExtras1 = getPersistableBundle(
+ PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
+ final PersistableBundle launcherExtras1 = getPersistableBundle(
+ PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
+ ps1.setSuspended(true, "suspendingPackage1", appExtras1, launcherExtras1, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+
+ ps2.setSuspended(true, "suspendingPackage2", null, null, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+ ps3.setSuspended(false, "irrelevant", null, null, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
+
+ settingsUnderTest.writePackageRestrictionsLPr(0);
+
+ settingsUnderTest.mPackages.clear();
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
+ // now read and verify
+ settingsUnderTest.readPackageRestrictionsLPr(0);
+ final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1).
+ readUserState(0);
+ assertThat(readPus1.suspended, is(true));
+ assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
+ assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
+ assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
+ is(true));
+
+ final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2).
+ readUserState(0);
+ assertThat(readPus2.suspended, is(true));
+ assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
+ assertThat(readPus2.suspendedAppExtras, is(nullValue()));
+ assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
+
+ final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
+ readUserState(0);
+ assertThat(readPus3.suspended, is(false));
+ }
+
+ @Test
+ public void testPackageRestrictionsSuspendedDefault() {
+ final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
+ assertThat(defaultSetting.getSuspended(0), is(false));
+ }
+
@Test
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
@@ -686,6 +772,26 @@
null /*usesStaticLibrariesVersions*/);
}
+ private PackageSetting createPackageSetting(String packageName) {
+ return new PackageSetting(
+ packageName,
+ packageName,
+ INITIAL_CODE_PATH /*codePath*/,
+ INITIAL_CODE_PATH /*resourcePath*/,
+ null /*legacyNativeLibraryPathString*/,
+ "x86_64" /*primaryCpuAbiString*/,
+ "x86" /*secondaryCpuAbiString*/,
+ null /*cpuAbiOverrideString*/,
+ INITIAL_VERSION_CODE,
+ 0,
+ 0 /*privateFlags*/,
+ null /*parentPackageName*/,
+ null /*childPackageNames*/,
+ 0,
+ null /*usesStaticLibraries*/,
+ null /*usesStaticLibrariesVersions*/);
+ }
+
private @NonNull List<UserInfo> createFakeUsers() {
ArrayList<UserInfo> users = new ArrayList<>();
users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
@@ -718,13 +824,13 @@
+ "<item name=\"android.permission.ACCESS_WIMAX_STATE\" package=\"android\" />"
+ "<item name=\"android.permission.REBOOT\" package=\"android\" protection=\"18\" />"
+ "</permissions>"
- + "<package name=\"com.google.app1\" codePath=\"/system/app/app1.apk\" nativeLibraryPath=\"/data/data/com.google.app1/lib\" flags=\"1\" ft=\"1360e2caa70\" it=\"135f2f80d08\" ut=\"1360e2caa70\" version=\"1109\" sharedUserId=\"11000\">"
+ + "<package name=\"com.android.app1\" codePath=\"/system/app/app1.apk\" nativeLibraryPath=\"/data/data/com.android.app1/lib\" flags=\"1\" ft=\"1360e2caa70\" it=\"135f2f80d08\" ut=\"1360e2caa70\" version=\"1109\" sharedUserId=\"11000\">"
+ "<sigs count=\"1\">"
+ "<cert index=\"0\" key=\"" + KeySetStrings.ctsKeySetCertA + "\" />"
+ "</sigs>"
+ "<proper-signing-keyset identifier=\"1\" />"
+ "</package>"
- + "<package name=\"com.google.app2\" codePath=\"/system/app/app2.apk\" nativeLibraryPath=\"/data/data/com.google.app2/lib\" flags=\"1\" ft=\"1360e578718\" it=\"135f2f80d08\" ut=\"1360e578718\" version=\"15\" enabled=\"3\" userId=\"11001\">"
+ + "<package name=\"com.android.app2\" codePath=\"/system/app/app2.apk\" nativeLibraryPath=\"/data/data/com.android.app2/lib\" flags=\"1\" ft=\"1360e578718\" it=\"135f2f80d08\" ut=\"1360e578718\" version=\"15\" enabled=\"3\" userId=\"11001\">"
+ "<sigs count=\"1\">"
+ "<cert index=\"0\" />"
+ "</sigs>"
@@ -774,11 +880,26 @@
+ "</packages>").getBytes());
}
+ private void writePackageRestrictions_oldSuspendInfoXml(final int userId) {
+ writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/"
+ + userId + "/package-restrictions.xml"),
+ ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<package-restrictions>\n"
+ + " <pkg name=\"" + PACKAGE_NAME_1 + "\" suspended=\"true\" />"
+ + " <pkg name=\"" + PACKAGE_NAME_2 + "\" suspended=\"false\" />"
+ + " <preferred-activities />\n"
+ + " <persistent-preferred-activities />\n"
+ + " <crossProfile-intent-filters />\n"
+ + " <default-apps />\n"
+ + "</package-restrictions>\n")
+ .getBytes());
+ }
+
private void writeStoppedPackagesXml() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<stopped-packages>"
- + "<pkg name=\"com.google.app1\" nl=\"1\" />"
+ + "<pkg name=\"com.android.app1\" nl=\"1\" />"
+ "<pkg name=\"com.android.app3\" nl=\"1\" />"
+ "</stopped-packages>")
.getBytes());
@@ -786,8 +907,8 @@
private void writePackagesList() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.list"),
- ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
- + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
+ ( "com.android.app1 11000 0 /data/data/com.android.app1 seinfo1"
+ + "com.android.app2 11001 0 /data/data/com.android.app2 seinfo2"
+ "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
.getBytes());
}
@@ -828,6 +949,11 @@
});
}
+ @After
+ public void tearDown() throws Exception {
+ deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
+ }
+
private void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
ArrayMap<String, PackageSetting> packages = settings.mPackages;
@@ -871,9 +997,9 @@
assertThat(KeySetUtils.getLastIssuedKeySetId(ksms), is(4L));
/* verify packages have been given the appropriate information */
- PackageSetting ps = packages.get("com.google.app1");
+ PackageSetting ps = packages.get("com.android.app1");
assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
- ps = packages.get("com.google.app2");
+ ps = packages.get("com.android.app2");
assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
assertThat(ps.keySetData.getAliases().get("AB"), is(4L));
ps = packages.get("com.android.app3");
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 50be8db..4e1418c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -23,8 +23,9 @@
import static org.junit.Assert.assertThat;
import android.content.pm.PackageUserState;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import org.junit.Test;
@@ -170,4 +171,44 @@
testUserState03.enabledComponents.add("com.android.unit_test_04");
assertThat(testUserState03.equals(oldUserState), is(false));
}
+
+ @Test
+ public void testPackageUserState05() {
+ PersistableBundle appExtras1 = new PersistableBundle();
+ PersistableBundle appExtras2 = new PersistableBundle();
+ appExtras1.putInt("appExtraId", 1);
+ appExtras2.putInt("appExtraId", 2);
+ PersistableBundle launcherExtras1 = new PersistableBundle();
+ PersistableBundle launcherExtras2 = new PersistableBundle();
+ launcherExtras1.putString("name", "launcherExtras1");
+ launcherExtras2.putString("name", "launcherExtras2");
+ final String suspendingPackage1 = "package1";
+ final String suspendingPackage2 = "package2";
+
+ final PackageUserState testUserState1 = new PackageUserState();
+ testUserState1.suspended = true;
+ testUserState1.suspendedAppExtras = appExtras1;
+ testUserState1.suspendedLauncherExtras = launcherExtras1;
+ testUserState1.suspendingPackage = suspendingPackage1;
+
+ final PackageUserState testUserState2 = new PackageUserState(testUserState1);
+ assertThat(testUserState1.equals(testUserState2), is(true));
+ testUserState2.suspendingPackage = suspendingPackage2;
+ assertThat(testUserState1.equals(testUserState2), is(false));
+
+ testUserState2.suspendingPackage = testUserState1.suspendingPackage;
+ testUserState2.suspendedAppExtras = appExtras2;
+ assertThat(testUserState1.equals(testUserState2), is(false));
+
+ testUserState2.suspendedAppExtras = testUserState1.suspendedAppExtras;
+ testUserState2.suspendedLauncherExtras = launcherExtras2;
+ assertThat(testUserState1.equals(testUserState2), is(false));
+
+ // Everything is different but irrelevant if suspended is false
+ testUserState2.suspended = testUserState1.suspended = false;
+ testUserState2.suspendedAppExtras = appExtras2;
+ testUserState2.suspendingPackage = suspendingPackage2;
+ assertThat(testUserState1.equals(testUserState2), is(true));
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
new file mode 100644
index 0000000..d702318
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class SuspendPackagesTest {
+ private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME;
+ private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME};
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private Handler mReceiverHandler;
+ private ComponentName mTestReceiverComponent;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mPackageManager = mContext.getPackageManager();
+ mPackageManager.setPackagesSuspended(PACKAGES_TO_SUSPEND, false, null, null, null);
+ mReceiverHandler = new Handler(Looper.getMainLooper());
+ mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
+ SuspendTestReceiver.class.getCanonicalName());
+ }
+
+ private Bundle requestAppAction(String action) throws InterruptedException {
+ final AtomicReference<Bundle> result = new AtomicReference<>();
+ final CountDownLatch receiverLatch = new CountDownLatch(1);
+
+ final Intent broadcastIntent = new Intent(action)
+ .setComponent(mTestReceiverComponent)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ result.set(getResultExtras(true));
+ receiverLatch.countDown();
+ }
+ }, mReceiverHandler, 0, null, null);
+
+ assertTrue("Test receiver timed out ", receiverLatch.await(5, TimeUnit.SECONDS));
+ return result.get();
+ }
+
+ private PersistableBundle getExtras(String keyPrefix, long lval, String sval, double dval) {
+ final PersistableBundle extras = new PersistableBundle(3);
+ extras.putLong(keyPrefix + ".LONG_VALUE", lval);
+ extras.putDouble(keyPrefix + ".DOUBLE_VALUE", dval);
+ extras.putString(keyPrefix + ".STRING_VALUE", sval);
+ return extras;
+ }
+
+ private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras) {
+ final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
+ PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, null);
+ assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
+ }
+
+ @Test
+ public void testIsPackageSuspended() {
+ suspendTestPackage(null, null);
+ assertTrue("isPackageSuspended is false",
+ mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testSuspendedStateFromApp() throws Exception {
+ Bundle resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
+ assertFalse(resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED, true));
+ assertNull(resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+
+ final PersistableBundle appExtras = getExtras("appExtras", 20, "20", 0.2);
+ suspendTestPackage(appExtras, null);
+
+ resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
+ assertTrue("resultFromApp:suspended is false",
+ resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED));
+ final PersistableBundle receivedAppExtras =
+ resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS);
+ receivedAppExtras.get(""); // hack to unparcel the bundles
+ appExtras.get("");
+ assertTrue("Received app extras " + receivedAppExtras + " different to the ones supplied",
+ BaseBundle.kindofEquals(appExtras, receivedAppExtras));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 26a7313..64501e4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -68,6 +68,7 @@
super.setUp();
MockitoAnnotations.initMocks(this);
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ mAdapter.setCallingPid(123);
sWm.mH.runWithScissors(() -> {
mHandler = new TestHandler(null, mClock);
}, 0);
@@ -83,7 +84,7 @@
new Point(50, 100), new Rect(50, 100, 150, 150));
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
-
+ sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -167,4 +168,33 @@
mController.goodToGo();
verifyZeroInteractions(mMockRunner);
}
+
+ @Test
+ public void testNotReallyStarted() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ mController.goodToGo();
+ verifyZeroInteractions(mMockRunner);
+ }
+
+ @Test
+ public void testOneNotStarted() throws Exception {
+ final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
+ final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+ mController.createAnimationAdapter(win1.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6506872..16b8458 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -134,6 +134,7 @@
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verifyZeroInteractions(mSpec);
assertAnimating(mAnimatable);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
verify(mSpec).startAnimation(any(), any(), any());
}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
new file mode 100644
index 0000000..40a34b9
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := SuspendTestApp
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..70a1fd0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.servicestests.apps.suspendtestapp">
+
+ <application>
+ <activity android:name=".SuspendTestActivity"
+ android:exported="true" />
+ <receiver android:name=".SuspendTestReceiver"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
new file mode 100644
index 0000000..fa5fc58
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.servicestests.apps.suspendtestapp;
+
+import android.app.Activity;
+
+public class SuspendTestActivity extends Activity {
+ private static final String TAG = SuspendTestActivity.class.getSimpleName();
+
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
new file mode 100644
index 0000000..6f353a0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * 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.servicestests.apps.suspendtestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+public class SuspendTestReceiver extends BroadcastReceiver {
+ private static final String TAG = SuspendTestReceiver.class.getSimpleName();
+
+ public static final String PACKAGE_NAME = "com.android.servicestests.apps.suspendtestapp";
+ public static final String ACTION_GET_SUSPENDED_STATE =
+ PACKAGE_NAME + ".action.GET_SUSPENDED_STATE";
+ public static final String EXTRA_SUSPENDED = PACKAGE_NAME + ".extra.SUSPENDED";
+ public static final String EXTRA_SUSPENDED_APP_EXTRAS =
+ PACKAGE_NAME + ".extra.SUSPENDED_APP_EXTRAS";
+
+ private PackageManager mPm;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mPm = context.getPackageManager();
+ Log.d(TAG, "Received request action " + intent.getAction());
+ switch (intent.getAction()) {
+ case ACTION_GET_SUSPENDED_STATE:
+ final Bundle result = new Bundle();
+ final boolean suspended = mPm.isPackageSuspended();
+ final PersistableBundle appExtras = mPm.getSuspendedPackageAppExtras();
+ result.putBoolean(EXTRA_SUSPENDED, suspended);
+ result.putParcelable(EXTRA_SUSPENDED_APP_EXTRAS, appExtras);
+ setResult(0, null, result);
+ break;
+ default:
+ Log.e(TAG, "Unknown action: " + intent.getAction());
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 73243d2..1c0e260 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -881,7 +881,7 @@
*/
@IntDef(prefix = { "HANDOVER_" },
value = {HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_NOT_SUPPORTED,
- HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERG_CALL,
+ HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL,
HANDOVER_FAILURE_UNKNOWN})
@Retention(RetentionPolicy.SOURCE)
public @interface HandoverFailureErrors {}
@@ -939,7 +939,7 @@
* For more information on call handovers, see
* {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
*/
- public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4;
+ public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4;
/**
* Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when a handover
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index eebe2a1..c79eec0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2227,7 +2227,9 @@
/**
* Gets the configuration values for a particular subscription, which is associated with a
* specific SIM card. If an invalid subId is used, the returned config will contain default
- * values.
+ * values. After using this method to get the configuration bundle,
+ * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+ * any carrier specific configuration has been applied.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2254,7 +2256,9 @@
}
/**
- * Gets the configuration values for the default subscription.
+ * Gets the configuration values for the default subscription. After using this method to get
+ * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
+ * called to confirm whether any carrier specific configuration has been applied.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2283,6 +2287,9 @@
* <p>
* After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
* use this method to confirm whether any carrier specific configuration has been applied.
+ * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
+ * still needs to get the current configuration, it must use this method to verify whether the
+ * configuration is default or carrier overridden.
* </p>
*
* @param bundle the configuration bundle to be checked.
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 8ebb77d..c2aca6b 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -54,6 +54,7 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -951,7 +952,8 @@
/** @hide */
@Override
- public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean hidden, int userId) {
+ public String[] setPackagesSuspended(String[] packageNames, boolean hidden,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
throw new UnsupportedOperationException();
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
new file mode 100644
index 0000000..4a83d1b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.connectivity;
+
+import static android.Manifest.permission.CHANGE_NETWORK_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PermissionMonitorTest {
+ private static final int MOCK_UID = 10001;
+ private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+
+ @Mock private Context mContext;
+ @Mock private PackageManager mPackageManager;
+
+ private PermissionMonitor mPermissionMonitor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
+ mPermissionMonitor = new PermissionMonitor(mContext, null);
+ }
+
+ private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
+ final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
+ when(mPackageManager.getPackageInfo(MOCK_PACKAGE_NAMES[0], GET_PERMISSIONS))
+ .thenReturn(packageInfo);
+ }
+
+ private PackageInfo packageInfoWithPermissions(String[] permissions, boolean preinstalled) {
+ final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.requestedPermissions = permissions;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags = preinstalled ? FLAG_SYSTEM : 0;
+ return packageInfo;
+ }
+
+ @Test
+ public void testHasPermission() {
+ PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = packageInfoWithPermissions(new String[] {
+ CHANGE_NETWORK_STATE, NETWORK_STACK
+ }, false);
+ assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = packageInfoWithPermissions(new String[] {
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
+ }, false);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+ }
+
+ @Test
+ public void testIsPreinstalledSystemApp() {
+ PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+ assertFalse(mPermissionMonitor.isPreinstalledSystemApp(app));
+
+ app = packageInfoWithPermissions(new String[] {}, true);
+ assertTrue(mPermissionMonitor.isPreinstalledSystemApp(app));
+ }
+
+ @Test
+ public void testHasUseBackgroundNetworksPermission() throws Exception {
+ expectPermission(new String[] { CHANGE_NETWORK_STATE }, false);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ // TODO : make this false when b/31479477 is fixed
+ expectPermission(new String[] {}, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+ expectPermission(new String[] { CHANGE_WIFI_STATE }, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, true);
+ assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] {}, false);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+ expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+ }
+}
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
index 3b0221c..79469e3 100644
--- a/tests/testables/tests/Android.mk
+++ b/tests/testables/tests/Android.mk
@@ -41,5 +41,7 @@
LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_PACKAGE)