Merge "Fix link-type check warning on com.android.mediadrm.signer"
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index b7b87dd..6156a0c 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -16,6 +16,7 @@
LOCAL_JAVA_LIBRARIES := android.test.base
LOCAL_PACKAGE_NAME := CorePerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 2db0dd6..a803369 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -24,6 +24,7 @@
ub-uiautomator
LOCAL_PACKAGE_NAME := MultiUserPerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/api/current.txt b/api/current.txt
index 41cf053..9412056 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1378,6 +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 textIsSelectable = 16843542; // 0x1010316
field public static final int textOff = 16843045; // 0x1010125
field public static final int textOn = 16843044; // 0x1010124
@@ -3882,6 +3883,7 @@
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+ method public boolean isBackgroundRestricted();
method public deprecated boolean isInLockTaskMode();
method public boolean isLowRamDevice();
method public static boolean isRunningInTestHarness();
@@ -6702,11 +6704,6 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
- field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
- field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
- field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
- field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
- field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -13596,7 +13593,7 @@
}
public class EmbossMaskFilter extends android.graphics.MaskFilter {
- ctor public EmbossMaskFilter(float[], float, float, float);
+ ctor public deprecated EmbossMaskFilter(float[], float, float, float);
}
public final class ImageDecoder implements java.lang.AutoCloseable {
@@ -13609,16 +13606,16 @@
method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source) throws java.io.IOException;
method public android.util.Size getSampledSize(int);
- method public void setAllocator(int);
- method public void setAsAlphaMask(boolean);
- method public void setCrop(android.graphics.Rect);
- method public void setMutable(boolean);
- method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
- method public void setPostProcessor(android.graphics.PostProcessor);
- method public void setPreferRamOverQuality(boolean);
- method public void setRequireUnpremultiplied(boolean);
- method public void setResize(int, int);
- method public void setResize(int);
+ method public android.graphics.ImageDecoder setAllocator(int);
+ method public android.graphics.ImageDecoder setAsAlphaMask(boolean);
+ method public android.graphics.ImageDecoder setConserveMemory(boolean);
+ method public android.graphics.ImageDecoder setCrop(android.graphics.Rect);
+ method public android.graphics.ImageDecoder setMutable(boolean);
+ method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
+ method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor);
+ method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
+ method public android.graphics.ImageDecoder setResize(int, int);
+ method public android.graphics.ImageDecoder setResize(int);
field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
@@ -14820,6 +14817,10 @@
method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
method public int describeContents();
+ method public int getResId();
+ method public java.lang.String getResPackage();
+ method public int getType();
+ method public android.net.Uri getUri();
method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
method public void loadDrawableAsync(android.content.Context, android.os.Message);
method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
@@ -14828,6 +14829,11 @@
method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
+ field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+ field public static final int TYPE_BITMAP = 1; // 0x1
+ field public static final int TYPE_DATA = 3; // 0x3
+ field public static final int TYPE_RESOURCE = 2; // 0x2
+ field public static final int TYPE_URI = 4; // 0x4
}
public static abstract interface Icon.OnDrawableLoadedListener {
@@ -22751,6 +22757,7 @@
method public abstract void close();
method public android.graphics.Rect getCropRect();
method public abstract int getFormat();
+ method public android.hardware.HardwareBuffer getHardwareBuffer();
method public abstract int getHeight();
method public abstract android.media.Image.Plane[] getPlanes();
method public abstract long getTimestamp();
@@ -23340,7 +23347,7 @@
field public static final int REGULAR_CODECS = 0; // 0x0
}
- public class MediaController2 implements java.lang.AutoCloseable {
+ public class MediaController2 implements java.lang.AutoCloseable android.media.MediaPlaylistController {
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);
@@ -23367,6 +23374,7 @@
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);
@@ -24467,14 +24475,56 @@
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 seekTo(long);
method public abstract void setAudioAttributes(android.media.AudioAttributes);
- field public static final int STATE_ERROR = 0; // 0x0
- field public static final int STATE_IDLE = 0; // 0x0
- field public static final int STATE_PAUSED = 0; // 0x0
- field public static final int STATE_PLAYING = 0; // 0x0
+ 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 setVolume(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 interface MediaPlaylistController {
+ method public abstract void addPlaylistItem(int, android.media.MediaItem2);
+ method public abstract android.media.MediaItem2 getCurrentPlaylistItem();
+ method public abstract java.util.List<android.media.MediaItem2> getPlaylist();
+ method public abstract void removePlaylistItem(android.media.MediaItem2);
+ method public abstract void replacePlaylistItem(int, android.media.MediaItem2);
+ method public abstract void skipToPlaylistItem(android.media.MediaItem2);
}
public class MediaRecorder implements android.media.AudioRouting {
@@ -24753,7 +24803,7 @@
method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
}
- public class MediaSession2 implements java.lang.AutoCloseable {
+ public class MediaSession2 implements java.lang.AutoCloseable android.media.MediaPlaylistController {
method public void addPlaylistItem(int, android.media.MediaItem2);
method public void close();
method public void editPlaylistItem(android.media.MediaItem2);
@@ -24769,6 +24819,7 @@
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);
@@ -24823,6 +24874,7 @@
public static final class MediaSession2.Builder {
ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase);
+ ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase, android.media.MediaPlaylistController);
method public android.media.MediaSession2 build();
method public android.media.MediaSession2.Builder setId(java.lang.String);
method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
@@ -25161,10 +25213,14 @@
method public android.media.AudioAttributes getAudioAttributes();
method public deprecated int getStreamType();
method public java.lang.String getTitle(android.content.Context);
+ method public float getVolume();
+ method public boolean isLooping();
method public boolean isPlaying();
method public void play();
method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+ method public void setLooping(boolean);
method public deprecated void setStreamType(int);
+ method public void setVolume(float);
method public void stop();
}
@@ -25520,6 +25576,7 @@
field public static final java.util.UUID EFFECT_TYPE_AEC;
field public static final java.util.UUID EFFECT_TYPE_AGC;
field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST;
+ field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING;
field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
@@ -25583,6 +25640,201 @@
field public short strength;
}
+ public final class DynamicsProcessing extends android.media.audiofx.AudioEffect {
+ ctor public DynamicsProcessing(int);
+ ctor public DynamicsProcessing(int, int, android.media.audiofx.DynamicsProcessing.Config);
+ method public android.media.audiofx.DynamicsProcessing.Channel getChannelByChannelIndex(int);
+ method public int getChannelCount();
+ method public android.media.audiofx.DynamicsProcessing.Config getConfig();
+ method public float getInputGainByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.Limiter getLimiterByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBandByChannelIndex(int, int);
+ method public android.media.audiofx.DynamicsProcessing.Mbc getMbcByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBandByChannelIndex(int, int);
+ method public android.media.audiofx.DynamicsProcessing.Eq getPostEqByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBandByChannelIndex(int, int);
+ method public android.media.audiofx.DynamicsProcessing.Eq getPreEqByChannelIndex(int);
+ method public void setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel);
+ method public void setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel);
+ method public void setInputGainAllChannelsTo(float);
+ method public void setInputGainbyChannel(int, float);
+ method public void setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter);
+ method public void setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter);
+ method public void setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc);
+ method public void setMbcBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public void setMbcBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public void setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc);
+ method public void setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPostEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPostEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPreEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPreEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ field public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION = 0; // 0x0
+ field public static final int VARIANT_FAVOR_TIME_RESOLUTION = 1; // 0x1
+ }
+
+ public static class DynamicsProcessing.BandBase {
+ ctor public DynamicsProcessing.BandBase(boolean, float);
+ method public float getCutoffFrequency();
+ method public boolean isEnabled();
+ method public void setCutoffFrequency(float);
+ method public void setEnabled(boolean);
+ }
+
+ public static class DynamicsProcessing.BandStage extends android.media.audiofx.DynamicsProcessing.Stage {
+ ctor public DynamicsProcessing.BandStage(boolean, boolean, int);
+ method public int getBandCount();
+ }
+
+ public static final class DynamicsProcessing.Channel {
+ ctor public DynamicsProcessing.Channel(float, boolean, int, boolean, int, boolean, int, boolean);
+ ctor public DynamicsProcessing.Channel(android.media.audiofx.DynamicsProcessing.Channel);
+ method public float getInputGain();
+ method public android.media.audiofx.DynamicsProcessing.Limiter getLimiter();
+ method public android.media.audiofx.DynamicsProcessing.Mbc getMbc();
+ method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBand(int);
+ method public android.media.audiofx.DynamicsProcessing.Eq getPostEq();
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBand(int);
+ method public android.media.audiofx.DynamicsProcessing.Eq getPreEq();
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBand(int);
+ method public void setInputGain(float);
+ method public void setLimiter(android.media.audiofx.DynamicsProcessing.Limiter);
+ method public void setMbc(android.media.audiofx.DynamicsProcessing.Mbc);
+ method public void setMbcBand(int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public void setPostEq(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPostEqBand(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPreEq(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPreEqBand(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ }
+
+ public static final class DynamicsProcessing.Config {
+ method public android.media.audiofx.DynamicsProcessing.Channel getChannelByChannelIndex(int);
+ method public float getInputGainByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.Limiter getLimiterByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBandByChannelIndex(int, int);
+ method public int getMbcBandCount();
+ method public android.media.audiofx.DynamicsProcessing.Mbc getMbcByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBandByChannelIndex(int, int);
+ method public int getPostEqBandCount();
+ method public android.media.audiofx.DynamicsProcessing.Eq getPostEqByChannelIndex(int);
+ method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBandByChannelIndex(int, int);
+ method public int getPreEqBandCount();
+ method public android.media.audiofx.DynamicsProcessing.Eq getPreEqByChannelIndex(int);
+ method public float getPreferredFrameDuration();
+ method public int getVariant();
+ method public boolean isLimiterInUse();
+ method public boolean isMbcInUse();
+ method public boolean isPostEqInUse();
+ method public boolean isPreEqInUse();
+ method public void setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel);
+ method public void setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel);
+ method public void setInputGainAllChannelsTo(float);
+ method public void setInputGainByChannelIndex(int, float);
+ method public void setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter);
+ method public void setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter);
+ method public void setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc);
+ method public void setMbcBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public void setMbcBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public void setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc);
+ method public void setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPostEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPostEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public void setPreEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPreEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand);
+ method public void setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ }
+
+ public static final class DynamicsProcessing.Config.Builder {
+ ctor public DynamicsProcessing.Config.Builder(int, int, boolean, int, boolean, int, boolean, int, boolean);
+ method public android.media.audiofx.DynamicsProcessing.Config build();
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setInputGainAllChannelsTo(float);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setInputGainByChannelIndex(int, float);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq);
+ method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreferredFrameDuration(float);
+ }
+
+ public static final class DynamicsProcessing.Eq extends android.media.audiofx.DynamicsProcessing.BandStage {
+ ctor public DynamicsProcessing.Eq(boolean, boolean, int);
+ ctor public DynamicsProcessing.Eq(android.media.audiofx.DynamicsProcessing.Eq);
+ method public android.media.audiofx.DynamicsProcessing.EqBand getBand(int);
+ method public void setBand(int, android.media.audiofx.DynamicsProcessing.EqBand);
+ }
+
+ public static final class DynamicsProcessing.EqBand extends android.media.audiofx.DynamicsProcessing.BandBase {
+ ctor public DynamicsProcessing.EqBand(boolean, float, float);
+ ctor public DynamicsProcessing.EqBand(android.media.audiofx.DynamicsProcessing.EqBand);
+ method public float getGain();
+ method public void setGain(float);
+ }
+
+ public static final class DynamicsProcessing.Limiter extends android.media.audiofx.DynamicsProcessing.Stage {
+ ctor public DynamicsProcessing.Limiter(boolean, boolean, int, float, float, float, float, float);
+ ctor public DynamicsProcessing.Limiter(android.media.audiofx.DynamicsProcessing.Limiter);
+ method public float getAttackTime();
+ method public int getLinkGroup();
+ method public float getPostGain();
+ method public float getRatio();
+ method public float getReleaseTime();
+ method public float getThreshold();
+ method public void setAttackTime(float);
+ method public void setLinkGroup(int);
+ method public void setPostGain(float);
+ method public void setRatio(float);
+ method public void setReleaseTime(float);
+ method public void setThreshold(float);
+ }
+
+ public static final class DynamicsProcessing.Mbc extends android.media.audiofx.DynamicsProcessing.BandStage {
+ ctor public DynamicsProcessing.Mbc(boolean, boolean, int);
+ ctor public DynamicsProcessing.Mbc(android.media.audiofx.DynamicsProcessing.Mbc);
+ method public android.media.audiofx.DynamicsProcessing.MbcBand getBand(int);
+ method public void setBand(int, android.media.audiofx.DynamicsProcessing.MbcBand);
+ }
+
+ public static final class DynamicsProcessing.MbcBand extends android.media.audiofx.DynamicsProcessing.BandBase {
+ ctor public DynamicsProcessing.MbcBand(boolean, float, float, float, float, float, float, float, float, float, float);
+ ctor public DynamicsProcessing.MbcBand(android.media.audiofx.DynamicsProcessing.MbcBand);
+ method public float getAttackTime();
+ method public float getExpanderRatio();
+ method public float getKneeWidth();
+ method public float getNoiseGateThreshold();
+ method public float getPostGain();
+ method public float getPreGain();
+ method public float getRatio();
+ method public float getReleaseTime();
+ method public float getThreshold();
+ method public void setAttackTime(float);
+ method public void setExpanderRatio(float);
+ method public void setKneeWidth(float);
+ method public void setNoiseGateThreshold(float);
+ method public void setPostGain(float);
+ method public void setPreGain(float);
+ method public void setRatio(float);
+ method public void setReleaseTime(float);
+ method public void setThreshold(float);
+ }
+
+ public static class DynamicsProcessing.Stage {
+ ctor public DynamicsProcessing.Stage(boolean, boolean);
+ method public boolean isEnabled();
+ method public boolean isInUse();
+ method public void setEnabled(boolean);
+ }
+
public class EnvironmentalReverb extends android.media.audiofx.AudioEffect {
ctor public EnvironmentalReverb(int, int) throws java.lang.IllegalArgumentException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
method public short getDecayHFRatio() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
@@ -33630,6 +33882,17 @@
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
+ field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
+ field public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5; // 0x5
+ field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
+ field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
+ field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6
+ field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
+ }
+
+ public static class UserManager.UserOperationException extends java.lang.RuntimeException {
+ method public int getUserOperationResult();
}
public abstract class VibrationEffect implements android.os.Parcelable {
@@ -53059,6 +53322,20 @@
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);
@@ -54449,6 +54726,27 @@
method public void suspend();
}
+ public class VideoView2 extends android.view.ViewGroup {
+ ctor public VideoView2(android.content.Context);
+ ctor public VideoView2(android.content.Context, android.util.AttributeSet);
+ ctor public VideoView2(android.content.Context, android.util.AttributeSet, int);
+ ctor public VideoView2(android.content.Context, android.util.AttributeSet, int, int);
+ method public android.widget.MediaControlView2 getMediaControlView2();
+ method public android.media.SessionToken2 getMediaSessionToken();
+ method public int getViewType();
+ method public boolean isSubtitleEnabled();
+ method public void setAudioAttributes(android.media.AudioAttributes);
+ method public void setAudioFocusRequest(int);
+ method public void setDataSource(android.media.DataSourceDesc);
+ method public void setMediaControlView2(android.widget.MediaControlView2, long);
+ method public void setMediaItem(android.media.MediaItem2);
+ method public void setSpeed(float);
+ method public void setSubtitleEnabled(boolean);
+ method public void setViewType(int);
+ field public static final int VIEW_TYPE_SURFACEVIEW = 1; // 0x1
+ field public static final int VIEW_TYPE_TEXTUREVIEW = 2; // 0x2
+ }
+
public class ViewAnimator extends android.widget.FrameLayout {
ctor public ViewAnimator(android.content.Context);
ctor public ViewAnimator(android.content.Context, android.util.AttributeSet);
diff --git a/api/system-current.txt b/api/system-current.txt
index bd1eef6..2f5af0d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -368,6 +368,7 @@
}
public final class StatsManager {
+ method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
@@ -4276,6 +4277,7 @@
method public byte[] getServerParams();
method public int getSnapshotVersion();
method public java.security.cert.CertPath getTrustedHardwareCertPath();
+ method public deprecated byte[] getTrustedHardwarePublicKey();
method public java.util.List<android.security.keystore.recovery.WrappedApplicationKey> getWrappedApplicationKeys();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainSnapshot> CREATOR;
@@ -4298,16 +4300,21 @@
public class RecoveryController {
method public android.security.keystore.recovery.RecoverySession createRecoverySession();
method public byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
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;
method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public 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;
method public void setRecoveryStatus(java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void setServerParams(byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void setSnapshotCreatedPendingIntent(android.app.PendingIntent) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -4319,6 +4326,7 @@
public class RecoverySession implements java.lang.AutoCloseable {
method public void close();
method public java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
+ method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
}
@@ -4328,6 +4336,7 @@
public final class WrappedApplicationKey implements android.os.Parcelable {
method public int describeContents();
+ method public deprecated byte[] getAccount();
method public java.lang.String getAlias();
method public byte[] getEncryptedKeyMaterial();
method public void writeToParcel(android.os.Parcel, int);
@@ -4337,6 +4346,7 @@
public static class WrappedApplicationKey.Builder {
ctor public WrappedApplicationKey.Builder();
method public android.security.keystore.recovery.WrappedApplicationKey build();
+ method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String);
method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]);
}
@@ -5984,7 +5994,6 @@
method public void setUiTtyMode(int, android.os.Message);
method public int shouldProcessCall(java.lang.String[]);
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
- field public static final int PROCESS_CALL_EMERGENCY_CSFB = 2; // 0x2
field public static final int PROCESS_CALL_IMS = 0; // 0x0
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 58652a2..48f43e0 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -91,34 +91,6 @@
}
-package android.security.keystore.recovery {
-
- public final class KeyChainSnapshot implements android.os.Parcelable {
- method public deprecated byte[] getTrustedHardwarePublicKey();
- }
-
- public class RecoveryController {
- method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
- method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
- }
-
- public class RecoverySession implements java.lang.AutoCloseable {
- method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
- }
-
- public final class WrappedApplicationKey implements android.os.Parcelable {
- method public deprecated byte[] getAccount();
- }
-
- public static class WrappedApplicationKey.Builder {
- method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
- }
-
-}
-
package android.service.notification {
public abstract class NotificationListenerService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index d5b4311..bc43692 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12,6 +12,7 @@
public class ActivityManager {
method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method public int getPackageImportance(java.lang.String);
+ method public long getTotalRam();
method public int getUidImportance(int);
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
@@ -47,7 +48,10 @@
public class AppOpsManager {
method public static java.lang.String[] getOpStrs();
+ method public boolean isOperationActive(int, int, java.lang.String);
method public void setMode(int, int, java.lang.String, int);
+ method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
+ method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
@@ -89,6 +93,12 @@
field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
+ field public static final int OP_RECORD_AUDIO = 27; // 0x1b
+ field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
+ }
+
+ public static abstract interface AppOpsManager.OnOpActiveChangedListener {
+ method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
}
public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -351,6 +361,7 @@
public final class DisplayManager {
method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+ method public android.graphics.Point getStableDisplaySize();
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
}
@@ -446,6 +457,10 @@
package android.os {
+ public static class Build.VERSION {
+ field public static final int RESOURCES_SDK_INT;
+ }
+
public class IncidentManager {
method public void reportIncident(android.os.IncidentReportArgs);
method public void reportIncident(java.lang.String, byte[]);
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index e87a78e..84a04e5 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -297,6 +297,10 @@
super.backupFinished(status);
System.out.println("Backup finished with result: "
+ convertBackupStatusToString(status));
+ if (status == BackupManager.ERROR_BACKUP_CANCELLED) {
+ System.out.println("Backups can be cancelled if a backup is already running, check "
+ + "backup dumpsys");
+ }
}
}
@@ -318,7 +322,7 @@
case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
return "Size quota exceeded";
case BackupManager.ERROR_BACKUP_CANCELLED:
- return "Backup Cancelled";
+ return "Backup cancelled";
default:
return "Unknown error";
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 54785ca..8ffe5bf 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -29,11 +29,11 @@
#include <signal.h>
#include <time.h>
+#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <androidfw/AssetManager.h>
#include <binder/IPCThreadState.h>
-#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 6bdd9be..3a47fe1 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -15,7 +15,8 @@
LOCAL_PATH:= $(call my-dir)
# proto files used in incidentd to generate cppstream proto headers.
-PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto
+PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto \
+ frameworks/base/core/proto/android/os/data.proto
# ========= #
# incidentd #
@@ -131,7 +132,7 @@
LOCAL_MODULE_CLASS := NATIVE_TESTS
gen_src_dir := $(local-generated-sources-dir)
# generate cppstream proto for testing
-GEN_PROTO := $(gen_src_dir)/log.proto.timestamp
+GEN_PROTO := $(gen_src_dir)/test.proto.timestamp
$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index 1bd1468..6dd8114 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -15,7 +15,7 @@
service incidentd /system/bin/incidentd
class main
user incidentd
- group incidentd log
+ group incidentd log readproc
on post-fs-data
# Create directory for incidentd
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index db60794..64da677 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -76,6 +76,7 @@
return -errno;
}
} else if (amt == 0) {
+ VLOG("Reached EOF of fd=%d", fd);
break;
}
mBuffer.wp()->move(amt);
@@ -156,10 +157,10 @@
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
VLOG("Fail to read fd %d: %s", fd, strerror(errno));
return -errno;
- } // otherwise just continue
- } else if (amt == 0) { // reach EOF so don't have to poll pfds[0].
- ::close(pfds[0].fd);
- pfds[0].fd = -1;
+ } // otherwise just continue
+ } else if (amt == 0) {
+ VLOG("Reached EOF of input file %d", fd);
+ pfds[0].fd = -1; // reach EOF so don't have to poll pfds[0].
} else {
rpos += amt;
cirSize += amt;
@@ -187,6 +188,7 @@
// if buffer is empty and fd is closed, close write fd.
if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) {
+ VLOG("Close write pipe %d", toFd);
::close(pfds[1].fd);
pfds[1].fd = -1;
}
@@ -207,6 +209,7 @@
return -errno;
} // otherwise just continue
} else if (amt == 0) {
+ VLOG("Reached EOF of fromFd %d", fromFd);
break;
} else {
mBuffer.wp()->move(amt);
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 5bfa093..66a3de1 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -26,7 +26,7 @@
using namespace std;
/**
- * Reads a file into a buffer, and then writes that data to an FdSet.
+ * Reads data from fd into a buffer, fd must be closed explicitly.
*/
class FdBuffer {
public:
@@ -83,6 +83,11 @@
*/
EncodedBuffer::iterator data() const;
+ /**
+ * Return the internal buffer, don't call unless you are familiar with EncodedBuffer.
+ */
+ EncodedBuffer* getInternalBuffer() { return &mBuffer; }
+
private:
EncodedBuffer mBuffer;
int64_t mStartTime;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 64eae3a..334d77c 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -18,12 +18,8 @@
#include "Section.h"
-#include <errno.h>
-#include <sys/prctl.h>
-#include <unistd.h>
#include <wait.h>
-#include <memory>
#include <mutex>
#include <android-base/file.h>
@@ -37,6 +33,7 @@
#include "FdBuffer.h"
#include "Privacy.h"
#include "PrivacyBuffer.h"
+#include "frameworks/base/core/proto/android/os/data.proto.h"
#include "frameworks/base/core/proto/android/util/log.proto.h"
#include "incidentd_util.h"
@@ -52,31 +49,11 @@
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
+const char GZIP[] = "/system/bin/gzip";
-static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe,
- Fpipe& c2pPipe) {
+static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) {
const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL};
- // fork used in multithreaded environment, avoid adding unnecessary code in child process
- pid_t pid = fork();
- if (pid == 0) {
- if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 || !p2cPipe.close() ||
- TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 || !c2pPipe.close()) {
- ALOGW("%s can't setup stdin and stdout for incident helper", name);
- _exit(EXIT_FAILURE);
- }
-
- /* make sure the child dies when incidentd dies */
- prctl(PR_SET_PDEATHSIG, SIGKILL);
-
- execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
-
- ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
- _exit(EXIT_FAILURE); // always exits with failure if any
- }
- // close the fds used in incident helper
- close(p2cPipe.readFd());
- close(c2pPipe.writeFd());
- return pid;
+ return fork_execute_cmd(INCIDENT_HELPER, const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
}
// ================================================================================
@@ -254,10 +231,12 @@
return NO_ERROR;
}
// ================================================================================
+static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; }
+
FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
: Section(id, timeoutMs), mFilename(filename) {
name = filename;
- mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
+ mIsSysfs = isSysfs(filename);
}
FileSection::~FileSection() {}
@@ -280,7 +259,7 @@
return -errno;
}
- pid_t pid = fork_execute_incident_helper(this->id, this->name.string(), p2cPipe, c2pPipe);
+ pid_t pid = fork_execute_incident_helper(this->id, &p2cPipe, &c2pPipe);
if (pid == -1) {
ALOGW("FileSection '%s' failed to fork", this->name.string());
return -errno;
@@ -289,6 +268,8 @@
// parent process
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
this->timeoutMs, mIsSysfs);
+ close(fd); // close the fd anyway.
+
if (readStatus != NO_ERROR || buffer.timedOut()) {
ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -313,7 +294,99 @@
return NO_ERROR;
}
+// ================================================================================
+GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) {
+ name = "gzip ";
+ name += filename;
+ va_list args;
+ va_start(args, filename);
+ mFilenames = varargs(filename, args);
+ va_end(args);
+}
+GZipSection::~GZipSection() {}
+
+status_t GZipSection::Execute(ReportRequestSet* requests) const {
+ // Reads the files in order, use the first available one.
+ int index = 0;
+ int fd = -1;
+ while (mFilenames[index] != NULL) {
+ fd = open(mFilenames[index], O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ break;
+ }
+ ALOGW("GZipSection failed to open file %s", mFilenames[index]);
+ index++; // look at the next file.
+ }
+ VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd);
+ if (fd == -1) return -1;
+
+ FdBuffer buffer;
+ Fpipe p2cPipe;
+ Fpipe c2pPipe;
+ // initiate pipes to pass data to/from gzip
+ if (!p2cPipe.init() || !c2pPipe.init()) {
+ ALOGW("GZipSection '%s' failed to setup pipes", this->name.string());
+ return -errno;
+ }
+
+ const char* gzipArgs[]{GZIP, NULL};
+ pid_t pid = fork_execute_cmd(GZIP, const_cast<char**>(gzipArgs), &p2cPipe, &c2pPipe);
+ if (pid == -1) {
+ ALOGW("GZipSection '%s' failed to fork", this->name.string());
+ return -errno;
+ }
+ // parent process
+
+ // construct Fdbuffer to output GZippedfileProto, the reason to do this instead of using
+ // ProtoOutputStream is to avoid allocation of another buffer inside ProtoOutputStream.
+ EncodedBuffer* internalBuffer = buffer.getInternalBuffer();
+ internalBuffer->writeHeader((uint32_t)GZippedFileProto::FILENAME, WIRE_TYPE_LENGTH_DELIMITED);
+ String8 usedFile(mFilenames[index]);
+ internalBuffer->writeRawVarint32(usedFile.size());
+ for (size_t i = 0; i < usedFile.size(); i++) {
+ internalBuffer->writeRawByte(mFilenames[index][i]);
+ }
+ internalBuffer->writeHeader((uint32_t)GZippedFileProto::GZIPPED_DATA,
+ WIRE_TYPE_LENGTH_DELIMITED);
+ size_t editPos = internalBuffer->wp()->pos();
+ internalBuffer->wp()->move(8); // reserve 8 bytes for the varint of the data size.
+ size_t dataBeginAt = internalBuffer->wp()->pos();
+ VLOG("GZipSection '%s' editPos=%zd, dataBeginAt=%zd", this->name.string(), editPos,
+ dataBeginAt);
+
+ status_t readStatus = buffer.readProcessedDataInStream(
+ fd, p2cPipe.writeFd(), c2pPipe.readFd(), this->timeoutMs, isSysfs(mFilenames[index]));
+ close(fd); // close the fd anyway.
+
+ if (readStatus != NO_ERROR || buffer.timedOut()) {
+ ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s",
+ this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ kill_child(pid);
+ return readStatus;
+ }
+
+ status_t gzipStatus = wait_child(pid);
+ if (gzipStatus != NO_ERROR) {
+ ALOGW("GZipSection '%s' abnormal child process: %s", this->name.string(),
+ strerror(-gzipStatus));
+ return gzipStatus;
+ }
+ // Revisit the actual size from gzip result and edit the internal buffer accordingly.
+ size_t dataSize = buffer.size() - dataBeginAt;
+ internalBuffer->wp()->rewind()->move(editPos);
+ internalBuffer->writeRawVarint32(dataSize);
+ internalBuffer->copy(dataBeginAt, dataSize);
+ VLOG("GZipSection '%s' wrote %zd bytes in %d ms, dataSize=%zd", this->name.string(),
+ buffer.size(), (int)buffer.durationMs(), dataSize);
+ status_t err = write_report_requests(this->id, buffer, requests);
+ if (err != NO_ERROR) {
+ ALOGW("GZipSection '%s' failed writing: %s", this->name.string(), strerror(-err));
+ return err;
+ }
+
+ return NO_ERROR;
+}
// ================================================================================
struct WorkerThreadData : public virtual RefBase {
const WorkerThreadSection* section;
@@ -457,42 +530,20 @@
}
// ================================================================================
-void CommandSection::init(const char* command, va_list args) {
- va_list copied_args;
- int numOfArgs = 0;
-
- va_copy(copied_args, args);
- while (va_arg(copied_args, const char*) != NULL) {
- numOfArgs++;
- }
- va_end(copied_args);
-
- // allocate extra 1 for command and 1 for NULL terminator
- mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
-
- mCommand[0] = command;
- name = command;
- for (int i = 0; i < numOfArgs; i++) {
- const char* arg = va_arg(args, const char*);
- mCommand[i + 1] = arg;
- name += " ";
- name += arg;
- }
- mCommand[numOfArgs + 1] = NULL;
-}
-
CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
: Section(id, timeoutMs) {
+ name = command;
va_list args;
va_start(args, command);
- init(command, args);
+ mCommand = varargs(command, args);
va_end(args);
}
CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
+ name = command;
va_list args;
va_start(args, command);
- init(command, args);
+ mCommand = varargs(command, args);
va_end(args);
}
@@ -527,7 +578,7 @@
strerror(errno));
_exit(err); // exit with command error code
}
- pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe);
+ pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe);
if (ihPid == -1) {
ALOGW("CommandSection '%s' failed to fork", this->name.string());
return -errno;
@@ -544,8 +595,7 @@
}
// TODO: wait for command here has one trade-off: the failed status of command won't be detected
- // until
- // buffer timeout, but it has advatage on starting the data stream earlier.
+ // until buffer timeout, but it has advatage on starting the data stream earlier.
status_t cmdStatus = wait_child(cmdPid);
status_t ihStatus = wait_child(ihPid);
if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index d644681..8294be1 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -84,6 +84,21 @@
};
/**
+ * Section that reads in a file and gzips the content.
+ */
+class GZipSection : public Section {
+public:
+ GZipSection(int id, const char* filename, ...);
+ virtual ~GZipSection();
+
+ virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+ // It looks up the content from multiple files and stops when the first one is available.
+ const char** mFilenames;
+};
+
+/**
* Base class for sections that call a command that might need a timeout.
*/
class WorkerThreadSection : public Section {
@@ -111,8 +126,6 @@
private:
const char** mCommand;
-
- void init(const char* command, va_list args);
};
/**
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 2415860..c095f2b 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define DEBUG false
+#include "Log.h"
+
#include "incidentd_util.h"
+#include <sys/prctl.h>
+
#include "section_list.h"
const Privacy* get_privacy_of_section(int id) {
@@ -50,4 +55,49 @@
int Fpipe::readFd() const { return mRead.get(); }
-int Fpipe::writeFd() const { return mWrite.get(); }
\ No newline at end of file
+int Fpipe::writeFd() const { return mWrite.get(); }
+
+pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) {
+ // fork used in multithreaded environment, avoid adding unnecessary code in child process
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (TEMP_FAILURE_RETRY(dup2(input->readFd(), STDIN_FILENO)) < 0 || !input->close() ||
+ TEMP_FAILURE_RETRY(dup2(output->writeFd(), STDOUT_FILENO)) < 0 || !output->close()) {
+ ALOGW("Can't setup stdin and stdout for command %s", cmd);
+ _exit(EXIT_FAILURE);
+ }
+
+ /* make sure the child dies when incidentd dies */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+ execv(cmd, argv);
+
+ ALOGW("%s failed in the child process: %s", cmd, strerror(errno));
+ _exit(EXIT_FAILURE); // always exits with failure if any
+ }
+ // close the fds used in child process.
+ close(input->readFd());
+ close(output->writeFd());
+ return pid;
+}
+// ================================================================================
+const char** varargs(const char* first, va_list rest) {
+ va_list copied_rest;
+ int numOfArgs = 1; // first is already count.
+
+ va_copy(copied_rest, rest);
+ while (va_arg(copied_rest, const char*) != NULL) {
+ numOfArgs++;
+ }
+ va_end(copied_rest);
+
+ // allocate extra 1 for NULL terminator
+ const char** ret = (const char**)malloc(sizeof(const char*) * (numOfArgs + 1));
+ ret[0] = first;
+ for (int i = 1; i < numOfArgs; i++) {
+ const char* arg = va_arg(rest, const char*);
+ ret[i] = arg;
+ }
+ ret[numOfArgs] = NULL;
+ return ret;
+}
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index 09aa040..db7ec82 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -20,12 +20,20 @@
#include <android-base/unique_fd.h>
+#include <stdarg.h>
+
#include "Privacy.h"
using namespace android::base;
+/**
+ * Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST;
+ */
const Privacy* get_privacy_of_section(int id);
+/**
+ * This class wraps android::base::Pipe.
+ */
class Fpipe {
public:
Fpipe();
@@ -41,4 +49,15 @@
unique_fd mWrite;
};
+/**
+ * Forks and exec a command with two pipes, one connects stdin for input,
+ * one connects stdout for output. It returns the pid of the child.
+ */
+pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output);
+
+/**
+ * Grabs varargs from stack and stores them in heap with NULL-terminated array.
+ */
+const char** varargs(const char* first, va_list rest);
+
#endif // INCIDENTD_UTIL_H
\ No newline at end of file
diff --git a/cmds/incidentd/testdata/kmsg.txt b/cmds/incidentd/testdata/kmsg.txt
new file mode 100644
index 0000000..a8e3c02
--- /dev/null
+++ b/cmds/incidentd/testdata/kmsg.txt
@@ -0,0 +1,47 @@
+[0] bldr_log_init: bldr_log_base=0x83600000, bldr_log_size=458752
+B - 626409 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B - 729255 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B - 729285 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0
+D - 104829 - APPSBL Image Loaded, Delta - (2498816 Bytes)
+B - 729468 - SBL1, End
+D - 643611 - SBL1, Delta
+S - Flash Throughput, 129000 KB/s (4729638 Bytes, 36613 us)
+S - DDR Frequency, 1017 MHz
+0x400, 0x400
+B - 482296 - Basic DDR tests done
+B - 544638 - clock_init, Start
+D - 244 - clock_init, Delta
+B - 544913 - HTC RPM DATARAM UPDATE info: Done
+B - 545004 - Image Load, Start
+B - 548359 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 5:0
+D - 3386 - QSEE Dev Config Image Loaded, Delta - (46232 Bytes)
+B - 548725 - Image Load, Start
+B - 550860 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 512:0
+D - 2166 - APDP Image Loaded, Delta - (7696 Bytes)
+B - 550891 - Image Load, Start
+B - 601612 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 7:0
+D - 50782 - QSEE Image Loaded, Delta - (1648640 Bytes)
+B - 601704 - Image Load, Start
+D - 244 - SEC Image Loaded, Delta - (4096 Bytes)
+B - 602344 - 0x1310 = 0x24
+B - 602375 - is_above_vbat_weak = 1, pon_reasons (with usb_in checked) = 0x31
+B - 602466 - sbl1_efs_handle_cookies, Start
+D - 91 - sbl1_efs_handle_cookies, Delta
+B - 602558 - Image Load, Start
+B - 613446 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 21:0
+D - 11010 - QHEE Image Loaded, Delta - (258280 Bytes)
+B - 613568 - Image Load, Start
+B - 624274 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 10:0
+D - 10736 - RPM Image Loaded, Delta - (224104 Bytes)
+B - 624335 - Image Load, Start
+D - 0 - STI Image Loaded, Delta - (0 Bytes)
+B - 624548 - Image Load, Start
+m_driver_init, Delta
+B - 471804 - pm_sbl_chg
+ ******************** [ START SECOND] ********************
+^@B - 736605 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B - 839451 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B - 839482 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0
+D - 104828 - APPSBL Image Loaded, Delta - (2498816 Bytes)
+B - 839665 - SBL1, End
+D - 753838 - SBL1, Delta
diff --git a/cmds/incidentd/testdata/kmsg.txt.gz b/cmds/incidentd/testdata/kmsg.txt.gz
new file mode 100644
index 0000000..fba449f
--- /dev/null
+++ b/cmds/incidentd/testdata/kmsg.txt.gz
Binary files differ
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 026bf74..1528224 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -39,10 +39,22 @@
using namespace android::os;
using namespace std;
using ::testing::StrEq;
+using ::testing::Test;
using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStdout;
// NOTICE: this test requires /system/bin/incident_helper is installed.
+class SectionTest : public Test {
+public:
+ virtual void SetUp() override { ASSERT_NE(tf.fd, -1); }
+
+protected:
+ TemporaryFile tf;
+ ReportRequestSet requests;
+
+ const std::string kTestPath = GetExecutableDirectory();
+ const std::string kTestDataPath = kTestPath + "/testdata/";
+};
class SimpleListener : public IIncidentReportStatusListener {
public:
@@ -58,10 +70,8 @@
virtual IBinder* onAsBinder() override { return nullptr; };
};
-TEST(SectionTest, HeaderSection) {
- TemporaryFile output2;
+TEST_F(SectionTest, HeaderSection) {
HeaderSection hs;
- ReportRequestSet requests;
IncidentReportArgs args1, args2;
args1.addSection(1);
@@ -77,7 +87,7 @@
args2.addHeader(head2);
requests.add(new ReportRequest(args1, new SimpleListener(), -1));
- requests.add(new ReportRequest(args2, new SimpleListener(), output2.fd));
+ requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd));
requests.setMainFd(STDOUT_FILENO);
string content;
@@ -87,28 +97,25 @@
"\x12\x3"
"axe\n\x05\x12\x03pup"));
- EXPECT_TRUE(ReadFileToString(output2.path, &content));
+ EXPECT_TRUE(ReadFileToString(tf.path, &content));
EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
}
-TEST(SectionTest, MetadataSection) {
+TEST_F(SectionTest, MetadataSection) {
MetadataSection ms;
- ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
requests.sectionStats(1)->set_success(true);
CaptureStdout();
ASSERT_EQ(NO_ERROR, ms.Execute(&requests));
- EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1"));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b(\x1"
+ "2\x4\b\x1\x10\x1"));
}
-TEST(SectionTest, FileSection) {
- TemporaryFile tf;
+TEST_F(SectionTest, FileSection) {
FileSection fs(REVERSE_PARSER, tf.path);
- ReportRequestSet requests;
- ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -120,66 +127,79 @@
EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai"));
}
-TEST(SectionTest, FileSectionTimeout) {
- TemporaryFile tf;
- // id -1 is timeout parser
+TEST_F(SectionTest, FileSectionTimeout) {
FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS);
- ReportRequestSet requests;
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
}
-TEST(SectionTest, CommandSectionConstructor) {
+TEST_F(SectionTest, GZipSection) {
+ const std::string testFile = kTestDataPath + "kmsg.txt";
+ const std::string testGzFile = testFile + ".gz";
+ GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL);
+
+ requests.setMainFd(tf.fd);
+ requests.setMainDest(android::os::DEST_LOCAL);
+
+ ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
+ std::string expect, gzFile, actual;
+ ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile));
+ ASSERT_TRUE(ReadFileToString(tf.path, &actual));
+ expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile;
+ EXPECT_THAT(actual, StrEq(expect));
+}
+
+TEST_F(SectionTest, GZipSectionNoFileFound) {
+ GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL);
+ requests.setMainFd(STDOUT_FILENO);
+ ASSERT_EQ(-1, gs.Execute(&requests));
+}
+
+TEST_F(SectionTest, CommandSectionConstructor) {
CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL);
CommandSection cs2(2, "single_command", NULL);
CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
CommandSection cs4(2, 43214, "single_command", NULL);
- EXPECT_THAT(cs1.name.string(), StrEq("echo \"this is a test\" ooo"));
+ EXPECT_THAT(cs1.name.string(), StrEq("echo"));
EXPECT_THAT(cs2.name.string(), StrEq("single_command"));
EXPECT_EQ(3123, cs3.timeoutMs);
EXPECT_EQ(43214, cs4.timeoutMs);
- EXPECT_THAT(cs3.name.string(), StrEq("echo \"this is a test\" ooo"));
+ EXPECT_THAT(cs3.name.string(), StrEq("echo"));
EXPECT_THAT(cs4.name.string(), StrEq("single_command"));
}
-TEST(SectionTest, CommandSectionEcho) {
+TEST_F(SectionTest, CommandSectionEcho) {
CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL);
- ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba"));
}
-TEST(SectionTest, CommandSectionCommandTimeout) {
+TEST_F(SectionTest, CommandSectionCommandTimeout) {
CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
- ReportRequestSet requests;
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
-TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
+TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) {
CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
- ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
-TEST(SectionTest, CommandSectionBadCommand) {
+TEST_F(SectionTest, CommandSectionBadCommand) {
CommandSection cs(NOOP_PARSER, "echoo", "about", NULL);
- ReportRequestSet requests;
ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
}
-TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
+TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) {
CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
- ReportRequestSet requests;
// timeout will return first
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
-TEST(SectionTest, LogSectionBinary) {
+TEST_F(SectionTest, LogSectionBinary) {
LogSection ls(1, LOG_ID_EVENTS);
- ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
@@ -187,9 +207,8 @@
EXPECT_FALSE(results.empty());
}
-TEST(SectionTest, LogSectionSystem) {
+TEST_F(SectionTest, LogSectionSystem) {
LogSection ls(1, LOG_ID_SYSTEM);
- ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
@@ -197,12 +216,9 @@
EXPECT_FALSE(results.empty());
}
-TEST(SectionTest, TestFilterPiiTaggedFields) {
- TemporaryFile tf;
+TEST_F(SectionTest, TestFilterPiiTaggedFields) {
FileSection fs(NOOP_PARSER, tf.path);
- ReportRequestSet requests;
- ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -212,11 +228,9 @@
EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
}
-TEST(SectionTest, TestBadFdRequest) {
- TemporaryFile input;
- FileSection fs(NOOP_PARSER, input.path);
- ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+TEST_F(SectionTest, TestBadFdRequest) {
+ FileSection fs(NOOP_PARSER, tf.path);
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
IncidentReportArgs args;
args.setAll(true);
@@ -231,11 +245,9 @@
EXPECT_EQ(badFdRequest->err, -EBADF);
}
-TEST(SectionTest, TestBadRequests) {
- TemporaryFile input;
- FileSection fs(NOOP_PARSER, input.path);
- ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+TEST_F(SectionTest, TestBadRequests) {
+ FileSection fs(NOOP_PARSER, tf.path);
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
IncidentReportArgs args;
args.setAll(true);
@@ -244,16 +256,14 @@
EXPECT_EQ(fs.Execute(&requests), -EBADF);
}
-TEST(SectionTest, TestMultipleRequests) {
- TemporaryFile input, output1, output2, output3;
- FileSection fs(NOOP_PARSER, input.path);
- ReportRequestSet requests;
+TEST_F(SectionTest, TestMultipleRequests) {
+ TemporaryFile output1, output2, output3;
+ FileSection fs(NOOP_PARSER, tf.path);
- ASSERT_TRUE(input.fd != -1);
ASSERT_TRUE(output1.fd != -1);
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
IncidentReportArgs args1, args2, args3;
args1.setAll(true);
@@ -286,17 +296,15 @@
EXPECT_THAT(content, StrEq(""));
}
-TEST(SectionTest, TestMultipleRequestsBySpec) {
- TemporaryFile input, output1, output2, output3;
- FileSection fs(NOOP_PARSER, input.path);
- ReportRequestSet requests;
+TEST_F(SectionTest, TestMultipleRequestsBySpec) {
+ TemporaryFile output1, output2, output3;
+ FileSection fs(NOOP_PARSER, tf.path);
- ASSERT_TRUE(input.fd != -1);
ASSERT_TRUE(output1.fd != -1);
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
IncidentReportArgs args1, args2, args3;
args1.setAll(true);
@@ -328,4 +336,4 @@
c = (char)STRING_FIELD_2.size();
EXPECT_TRUE(ReadFileToString(output3.path, &content));
EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 21f30e2..b0e2c43 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -217,6 +217,14 @@
const Field mMatcher;
const int32_t mMask;
+ inline const Field& getMatcher() const {
+ return mMatcher;
+ }
+
+ inline int32_t getMask() const {
+ return mMask;
+ }
+
bool hasAnyPositionMatcher(int* prefix) const {
if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) {
(*prefix) = mMatcher.getPrefix(2);
@@ -224,6 +232,10 @@
}
return false;
}
+
+ inline bool operator!=(const Matcher& that) const {
+ return mMatcher != that.getMatcher() || mMask != that.getMask();
+ };
};
/**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 1502a00..cc706313 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -166,10 +166,11 @@
}
}
-void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
+ const Metric2Condition& links,
vector<HashableDimensionKey>* conditionDimension) {
// Get the dimension first by using dimension from what.
- filterValues(links.metricFields, event.getValues(), conditionDimension);
+ filterValues(links.metricFields, eventValues, conditionDimension);
// Then replace the field with the dimension from condition.
for (auto& dim : *conditionDimension) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 5d016e9..57bdf68 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -144,7 +144,8 @@
void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values,
std::vector<FieldValue>* output);
-void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
+ const Metric2Condition& links,
std::vector<HashableDimensionKey>* conditionDimension);
} // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 087e596..9b58a14 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -74,7 +74,8 @@
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
mSendBroadcast(sendBroadcast),
- mTimeBaseSec(timeBaseSec) {
+ mTimeBaseSec(timeBaseSec),
+ mLastLogTimestamp(0) {
StatsPullerManager statsPullerManager;
statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
}
@@ -144,9 +145,12 @@
}
}
-// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ if (event->GetElapsedTimestampNs() < mLastLogTimestamp) {
+ return;
+ }
+ mLastLogTimestamp = event->GetElapsedTimestampNs();
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 4d9f185..7a6aa1e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -106,6 +106,8 @@
const long mTimeBaseSec;
+ int64_t mLastLogTimestamp;
+
long mLastPullerCacheClearTimeSec = 0;
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1c1d16b..42ae022 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -101,6 +101,9 @@
OverlayStateChanged overlay_state_changed = 59;
ForegroundServiceStateChanged foreground_service_state_changed = 60;
CallStateChanged call_state_changed = 61;
+ KeyguardStateChanged keyguard_state_changed = 62;
+ KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
+ KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -130,6 +133,9 @@
FullBatteryCapacity full_battery_capacity = 10020;
Temperature temperature = 10021;
}
+
+ // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
+ // 100,000 are reserved for non-AOSP (e.g. OEMs) to use.
}
/**
@@ -747,6 +753,63 @@
}
/**
+ * Logs keyguard state. The keyguard is the lock screen.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+ */
+message KeyguardStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ // The keyguard is hidden when the phone is unlocked.
+ HIDDEN = 1;
+ // The keyguard is shown when the phone is locked (screen turns off).
+ SHOWN= 2;
+ // The keyguard is occluded when something is overlaying the keyguard.
+ // Eg. Opening the camera while on the lock screen.
+ OCCLUDED = 3;
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs keyguard bouncer state. The bouncer is a part of the keyguard, and
+ * prompts the user to enter a password (pattern, pin, etc).
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+ */
+
+message KeyguardBouncerStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ // Bouncer is hidden, either as a result of successfully entering the
+ // password, screen timing out, or user going back to lock screen.
+ HIDDEN = 1;
+ // This is when the user is being prompted to enter the password.
+ SHOWN = 2;
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs the result of entering a password into the keyguard bouncer.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+ */
+message KeyguardBouncerPasswordEntered {
+ enum BouncerResult {
+ UNKNOWN = 0;
+ // The password entered was incorrect.
+ FAILURE = 1;
+ // The password entered was correct.
+ SUCCESS = 2;
+ }
+ optional BouncerResult result = 1;
+}
+
+/**
* Logs the duration of a davey (jank of >=700ms) when it occurs
*
* Logged from:
@@ -1532,4 +1595,4 @@
// Temperature in degrees C.
optional float temperature_C = 3;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 4612009..5d5e64e 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -87,6 +87,10 @@
const string& str_match) {
if (isAttributionUidField(field, value)) {
int uid = value.int_value;
+ auto aidIt = UidMap::sAidToUidMapping.find(str_match);
+ if (aidIt != UidMap::sAidToUidMapping.end()) {
+ return ((int)aidIt->second) == uid;
+ }
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
} else if (value.getType() == STRING) {
@@ -207,6 +211,9 @@
}
return false;
}
+ // Finally, we get to the point of real value matching.
+ // If the field matcher ends with ANY, then we have [start, end) range > 1.
+ // In the following, we should return true, when ANY of the values matches.
case FieldValueMatcher::ValueMatcherCase::kEqBool: {
for (int i = start; i < end; i++) {
if ((values[i].mValue.getType() == INT &&
@@ -225,9 +232,36 @@
return true;
}
}
- }
return false;
- case FieldValueMatcher::ValueMatcherCase::kEqInt:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kNeqAllString: {
+ const auto& str_list = matcher.neq_all_string();
+ for (int i = start; i < end; i++) {
+ bool notEqAll = true;
+ for (const auto& str : str_list.str_value()) {
+ if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ notEqAll = false;
+ break;
+ }
+ }
+ if (notEqAll) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
+ const auto& str_list = matcher.eq_any_string();
+ for (int i = start; i < end; i++) {
+ for (const auto& str : str_list.str_value()) {
+ if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(matcher.eq_int() == values[i].mValue.int_value)) {
@@ -240,7 +274,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kLtInt:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value < matcher.lt_int())) {
@@ -253,7 +288,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kGtInt:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value > matcher.gt_int())) {
@@ -266,7 +302,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kLtFloat:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value < matcher.lt_float())) {
@@ -274,7 +311,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kGtFloat:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value > matcher.gt_float())) {
@@ -282,7 +320,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kLteInt:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value <= matcher.lte_int())) {
@@ -295,7 +334,8 @@
}
}
return false;
- case FieldValueMatcher::ValueMatcherCase::kGteInt:
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value >= matcher.gte_int())) {
@@ -308,6 +348,7 @@
}
}
return false;
+ }
default:
return false;
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 16cac99..80329c3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -101,6 +101,18 @@
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ if (mDimensionsInWhat.size() == mInternalDimensions.size()) {
+ bool mUseWhatDimensionAsInternalDimension = true;
+ for (size_t i = 0; mUseWhatDimensionAsInternalDimension &&
+ i < mDimensionsInWhat.size(); ++i) {
+ if (mDimensionsInWhat[i] != mInternalDimensions[i]) {
+ mUseWhatDimensionAsInternalDimension = false;
+ }
+ }
+ } else {
+ mUseWhatDimensionAsInternalDimension = false;
+ }
+
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -141,29 +153,56 @@
flushIfNeededLocked(eventTime);
// Now for each of the on-going event, check if the condition has changed for them.
- for (auto& pair : mCurrentSlicedDurationTrackerMap) {
- pair.second->onSlicedConditionMayChange(eventTime);
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (auto& pair : whatIt.second) {
+ pair.second->onSlicedConditionMayChange(eventTime);
+ }
}
-
- std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
- mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
- &conditionDimensionsKeySet);
-
- for (auto& pair : mCurrentSlicedDurationTrackerMap) {
- conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+ if (mDimensionsInCondition.empty()) {
+ return;
}
- std::unordered_set<MetricDimensionKey> newKeys;
- for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
- for (auto& pair : mCurrentSlicedDurationTrackerMap) {
- auto newKey =
- MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
- if (newKeys.find(newKey) == newKeys.end()) {
- mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
- mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
- mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+
+ if (mMetric2ConditionLinks.empty()) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+ mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
+ &conditionDimensionsKeySet);
+ for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (const auto& pair : whatIt.second) {
+ conditionDimensionsKeySet.erase(pair.first);
}
- newKeys.insert(newKey);
+ }
+ for (const auto& conditionDimension : conditionDimensionsKeySet) {
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ if (!whatIt.second.empty()) {
+ unique_ptr<DurationTracker> newTracker =
+ whatIt.second.begin()->second->clone(eventTime);
+ newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
+ newTracker->onSlicedConditionMayChange(eventTime);
+ whatIt.second[conditionDimension] = std::move(newTracker);
+ }
+ }
+ }
+ } else {
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ ConditionKey conditionKey;
+ for (const auto& link : mMetric2ConditionLinks) {
+ getDimensionForCondition(whatIt.first.getValues(), link,
+ &conditionKey[link.conditionId]);
+ }
+ std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
+ mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+ &conditionDimensionsKeys);
+
+ for (const auto& conditionDimension : conditionDimensionsKeys) {
+ if (!whatIt.second.empty() &&
+ whatIt.second.find(conditionDimension) == whatIt.second.end()) {
+ auto newTracker = whatIt.second.begin()->second->clone(eventTime);
+ newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
+ newTracker->onSlicedConditionMayChange(eventTime);
+ whatIt.second[conditionDimension] = std::move(newTracker);
+ }
+ }
}
}
}
@@ -175,8 +214,10 @@
flushIfNeededLocked(eventTime);
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
- for (auto& pair : mCurrentSlicedDurationTrackerMap) {
- pair.second->onConditionChanged(conditionMet, eventTime);
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (auto& pair : whatIt.second) {
+ pair.second->onConditionChanged(conditionMet, eventTime);
+ }
}
}
@@ -241,13 +282,20 @@
return;
}
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDurationTrackerMap.begin();
- it != mCurrentSlicedDurationTrackerMap.end();) {
- if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s", it->first.c_str());
- it = mCurrentSlicedDurationTrackerMap.erase(it);
+ for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
+ whatIt != mCurrentSlicedDurationTrackerMap.end();) {
+ for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
+ if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
+ it = whatIt->second.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (whatIt->second.empty()) {
+ whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
- ++it;
+ whatIt++;
}
}
@@ -257,13 +305,20 @@
}
void DurationMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
- for (auto it = mCurrentSlicedDurationTrackerMap.begin();
- it != mCurrentSlicedDurationTrackerMap.end();) {
- if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s", it->first.c_str());
- it = mCurrentSlicedDurationTrackerMap.erase(it);
+ for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
+ whatIt != mCurrentSlicedDurationTrackerMap.end();) {
+ for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
+ if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
+ it = whatIt->second.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (whatIt->second.empty()) {
+ whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
- ++it;
+ whatIt++;
}
}
}
@@ -276,18 +331,16 @@
fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
(unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
- for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
- fprintf(out, "\t%s\n", slice.first.c_str());
- slice.second->dumpStates(out, verbose);
+ for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (const auto& slice : whatIt.second) {
+ fprintf(out, "\t%s\t%s\n", whatIt.first.c_str(), slice.first.c_str());
+ slice.second->dumpStates(out, verbose);
+ }
}
}
}
bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
- // the key is not new, we are good.
- if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
- return false;
- }
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -302,49 +355,162 @@
return false;
}
-void DurationMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition,
- const LogEvent& event) {
- flushIfNeededLocked(event.GetElapsedTimestampNs());
+void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKeys,
+ bool condition, const LogEvent& event) {
+ const auto& whatKey = eventKey.getDimensionKeyInWhat();
+ const auto& condKey = eventKey.getDimensionKeyInCondition();
- if (matcherIndex == mStopAllIndex) {
- for (auto& pair : mCurrentSlicedDurationTrackerMap) {
- pair.second->noteStopAll(event.GetElapsedTimestampNs());
- }
- return;
- }
-
- if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
+ auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
+ if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
+ mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
+ } else {
+ if (whatIt->second.find(condKey) == whatIt->second.end()) {
+ if (hitGuardRailLocked(eventKey)) {
+ return;
+ }
+ mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
+ }
}
- auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey);
+ if (mUseWhatDimensionAsInternalDimension) {
+ it->second->noteStart(whatKey, condition,
+ event.GetElapsedTimestampNs(), conditionKeys);
+ return;
+ }
std::vector<HashableDimensionKey> values;
filterValues(mInternalDimensions, event.getValues(), &values);
if (values.empty()) {
- if (matcherIndex == mStartIndex) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
- event.GetElapsedTimestampNs(), conditionKeys);
- } else if (matcherIndex == mStopIndex) {
- it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetElapsedTimestampNs(), false);
- }
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
+ event.GetElapsedTimestampNs(), conditionKeys);
} else {
for (const auto& value : values) {
- if (matcherIndex == mStartIndex) {
- it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys);
- } else if (matcherIndex == mStopIndex) {
- it->second->noteStop(value, event.GetElapsedTimestampNs(), false);
- }
+ it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys);
}
}
}
+void DurationMetricProducer::onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKeys, bool condition,
+ const LogEvent& event) {
+ ALOGW("Not used in duration tracker.");
+}
+
+void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
+ const LogEvent& event) {
+ uint64_t eventTimeNs = event.GetElapsedTimestampNs();
+ if (eventTimeNs < mStartTimeNs) {
+ return;
+ }
+
+ flushIfNeededLocked(event.GetElapsedTimestampNs());
+
+ // Handles Stopall events.
+ if (matcherIndex == mStopAllIndex) {
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (auto& pair : whatIt.second) {
+ pair.second->noteStopAll(event.GetElapsedTimestampNs());
+ }
+ }
+ return;
+ }
+
+ vector<HashableDimensionKey> dimensionInWhatValues;
+ if (!mDimensionsInWhat.empty()) {
+ filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+ } else {
+ dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
+ }
+
+ // Handles Stop events.
+ if (matcherIndex == mStopIndex) {
+ if (mUseWhatDimensionAsInternalDimension) {
+ for (const HashableDimensionKey& whatKey : dimensionInWhatValues) {
+ auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
+ if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+ for (const auto& condIt : whatIt->second) {
+ condIt.second->noteStop(whatKey, event.GetElapsedTimestampNs(), false);
+ }
+ }
+ }
+ return;
+ }
+
+ std::vector<HashableDimensionKey> internalDimensionKeys;
+ filterValues(mInternalDimensions, event.getValues(), &internalDimensionKeys);
+ if (internalDimensionKeys.empty()) {
+ internalDimensionKeys.push_back(DEFAULT_DIMENSION_KEY);
+ }
+ for (const HashableDimensionKey& whatDimension : dimensionInWhatValues) {
+ auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
+ if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+ for (const auto& condIt : whatIt->second) {
+ for (const auto& internalDimensionKey : internalDimensionKeys) {
+ condIt.second->noteStop(
+ internalDimensionKey, event.GetElapsedTimestampNs(), false);
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ bool condition;
+ ConditionKey conditionKey;
+ std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
+ if (mConditionSliced) {
+ for (const auto& link : mMetric2ConditionLinks) {
+ getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
+ }
+
+ auto conditionState =
+ mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+ &dimensionKeysInCondition);
+ condition = (conditionState == ConditionState::kTrue);
+ if (mDimensionsInCondition.empty() && condition) {
+ dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+ }
+ } else {
+ condition = mCondition;
+ if (condition) {
+ dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+ }
+ }
+
+ for (const auto& whatDimension : dimensionInWhatValues) {
+ auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
+ // If the what dimension is already there, we should update all the trackers even
+ // the condition is false.
+ if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+ for (const auto& condIt : whatIt->second) {
+ const bool cond = dimensionKeysInCondition.find(condIt.first) !=
+ dimensionKeysInCondition.end();
+ handleStartEvent(MetricDimensionKey(whatDimension, condIt.first),
+ conditionKey, cond, event);
+ }
+ } else {
+ // If it is a new what dimension key, we need to handle the start events for all current
+ // condition dimensions.
+ for (const auto& conditionDimension : dimensionKeysInCondition) {
+ handleStartEvent(MetricDimensionKey(whatDimension, conditionDimension),
+ conditionKey, condition, event);
+ }
+ }
+ if (dimensionKeysInCondition.empty()) {
+ handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
+ conditionKey, condition, event);
+ }
+ }
+}
+
+
size_t DurationMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index f41c278..73d074f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,12 +50,16 @@
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
protected:
+ void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) override;
private:
+ void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
+ bool condition, const LogEvent& event);
+
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
@@ -92,12 +96,16 @@
// The dimension from the atom predicate. e.g., uid, wakelock name.
vector<Matcher> mInternalDimensions;
+ // This boolean is true iff When mInternalDimensions == mDimensionsInWhat
+ bool mUseWhatDimensionAsInternalDimension;
+
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
- // The current bucket.
- std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+ // The duration trackers in the current bucket.
+ std::unordered_map<HashableDimensionKey,
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index f3307dc..18694a1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -37,7 +37,7 @@
std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(event, link, &conditionKey[link.conditionId]);
+ getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
@@ -48,37 +48,30 @@
condition = mCondition;
}
- vector<HashableDimensionKey> dimensionInWhatValues;
- if (mDimensionsInWhat.size() > 0) {
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+ if (mDimensionsInCondition.empty() && condition) {
+ dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
- if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
- onMatchedLogEventInternalLocked(
- matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
- } else if (dimensionKeysInCondition.empty()) {
- for (const HashableDimensionKey& whatValue : dimensionInWhatValues) {
- onMatchedLogEventInternalLocked(matcherIndex,
- MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
- }
- } else if (dimensionInWhatValues.empty()) {
+ vector<HashableDimensionKey> dimensionInWhatValues;
+ if (!mDimensionsInWhat.empty()) {
+ filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+ } else {
+ dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
+ }
+
+ for (const auto& whatDimension : dimensionInWhatValues) {
for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
onMatchedLogEventInternalLocked(
- matcherIndex,
- MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
- conditionKey, condition, event);
+ matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey),
+ conditionKey, condition, event);
}
- } else {
- for (const auto& whatValue : dimensionInWhatValues) {
- for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
- onMatchedLogEventInternalLocked(
- matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey),
- conditionKey, condition, event);
- }
+ if (dimensionKeysInCondition.empty()) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
+ conditionKey, condition, event);
}
}
-}
+ }
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 83e1740..2bf6241 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -233,7 +233,7 @@
const LogEvent& event) = 0;
// Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
+ virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
mutable std::mutex mMutex;
};
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index a313854..1e8aa12 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -74,6 +74,9 @@
int64 gte_int = 11;
MessageMatcher matches_tuple = 12;
+
+ StringListMatcher eq_any_string = 13;
+ StringListMatcher neq_all_string = 14;
}
}
@@ -81,6 +84,10 @@
repeated FieldValueMatcher field_value_matcher = 1;
}
+message StringListMatcher {
+ repeated string str_value = 1;
+}
+
enum LogicalOperation {
LOGICAL_OPERATION_UNSPECIFIED = 0;
AND = 1;
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 2320a9d..36c6e0c 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -294,6 +294,159 @@
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
+TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
+ UidMap uidMap;
+ uidMap.updateMap(
+ {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+
+ AttributionNodeInternal attribution_node1;
+ attribution_node1.set_uid(1111);
+ attribution_node1.set_tag("location1");
+
+ AttributionNodeInternal attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("location2");
+
+ AttributionNodeInternal attribution_node3;
+ attribution_node3.set_uid(3333);
+ attribution_node3.set_tag("location3");
+
+ AttributionNodeInternal attribution_node4;
+ attribution_node4.set_uid(1066);
+ attribution_node4.set_tag("location3");
+ std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
+ attribution_node3, attribution_node4};
+
+ // Set up the event
+ LogEvent event(TAG_ID, 0);
+ event.write(attribution_nodes);
+ event.write("some value");
+ // Convert to a LogEvent
+ event.init();
+
+ // Set up the matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+
+ // Match first node.
+ auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+ attributionMatcher->set_field(FIELD_ID_1);
+ attributionMatcher->set_position(Position::FIRST);
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+ ATTRIBUTION_UID_FIELD_ID);
+ auto neqStringList = attributionMatcher->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_neq_all_string();
+ neqStringList->add_str_value("pkg2");
+ neqStringList->add_str_value("pkg3");
+
+ auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+ fieldMatcher->set_field(FIELD_ID_2);
+ fieldMatcher->set_eq_string("some value");
+
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ neqStringList->Clear();
+ neqStringList->add_str_value("pkg1");
+ neqStringList->add_str_value("pkg3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::ANY);
+ neqStringList->Clear();
+ neqStringList->add_str_value("maps.com");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ neqStringList->Clear();
+ neqStringList->add_str_value("PkG3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::LAST);
+ neqStringList->Clear();
+ neqStringList->add_str_value("AID_STATSD");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
+ UidMap uidMap;
+ uidMap.updateMap(
+ {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+
+ AttributionNodeInternal attribution_node1;
+ attribution_node1.set_uid(1067);
+ attribution_node1.set_tag("location1");
+
+ AttributionNodeInternal attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("location2");
+
+ AttributionNodeInternal attribution_node3;
+ attribution_node3.set_uid(3333);
+ attribution_node3.set_tag("location3");
+
+ AttributionNodeInternal attribution_node4;
+ attribution_node4.set_uid(1066);
+ attribution_node4.set_tag("location3");
+ std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
+ attribution_node3, attribution_node4};
+
+ // Set up the event
+ LogEvent event(TAG_ID, 0);
+ event.write(attribution_nodes);
+ event.write("some value");
+ // Convert to a LogEvent
+ event.init();
+
+ // Set up the matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+
+ // Match first node.
+ auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+ attributionMatcher->set_field(FIELD_ID_1);
+ attributionMatcher->set_position(Position::FIRST);
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+ ATTRIBUTION_UID_FIELD_ID);
+ auto eqStringList = attributionMatcher->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_eq_any_string();
+ eqStringList->add_str_value("AID_ROOT");
+ eqStringList->add_str_value("AID_INCIDENTD");
+
+ auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+ fieldMatcher->set_field(FIELD_ID_2);
+ fieldMatcher->set_eq_string("some value");
+
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::ANY);
+ eqStringList->Clear();
+ eqStringList->add_str_value("AID_STATSD");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ eqStringList->Clear();
+ eqStringList->add_str_value("pkg1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ auto normalStringField = fieldMatcher->mutable_eq_any_string();
+ normalStringField->add_str_value("some value123");
+ normalStringField->add_str_value("some value");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ normalStringField->Clear();
+ normalStringField->add_str_value("AID_STATSD");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ eqStringList->Clear();
+ eqStringList->add_str_value("maps.com");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
TEST(AtomMatcherTest, TestBoolMatcher) {
UidMap uidMap;
// Set up the matcher
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 752b662..cc57f6c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -659,7 +659,6 @@
Landroid/graphics/drawable/GradientDrawable$GradientState;->mPadding:Landroid/graphics/Rect;
Landroid/graphics/drawable/GradientDrawable$GradientState;->mPositions:[F
Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/Icon;->getResPackage()Ljava/lang/String;
Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
@@ -977,8 +976,6 @@
Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
-Landroid/media/Ringtone;->setLooping(Z)V
-Landroid/media/Ringtone;->setVolume(F)V
Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 03faeee..de4d178 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -71,6 +71,7 @@
import com.android.internal.os.RoSystemProperties;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.MemInfoReader;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlSerializer;
@@ -964,6 +965,17 @@
}
/**
+ * Return the total number of bytes of RAM this device has.
+ * @hide
+ */
+ @TestApi
+ public long getTotalRam() {
+ MemInfoReader memreader = new MemInfoReader();
+ memreader.readMemInfo();
+ return memreader.getTotalSize();
+ }
+
+ /**
* Return the maximum number of recents entries that we will maintain and show.
* @hide
*/
@@ -3335,6 +3347,28 @@
}
/**
+ * Query whether the user has enabled background restrictions for this app.
+ *
+ * <p> The user may chose to do this, if they see that an app is consuming an unreasonable
+ * amount of battery while in the background. </p>
+ *
+ * <p> If true, any work that the app tries to do will be aggressively restricted while it is in
+ * the background. At a minimum, jobs and alarms will not execute and foreground services
+ * cannot be started unless an app activity is in the foreground. </p>
+ *
+ * <p><b> Note that these restrictions stay in effect even when the device is charging.</b></p>
+ *
+ * @return true if user has enforced background restrictions for this app, false otherwise.
+ */
+ public boolean isBackgroundRestricted() {
+ try {
+ return getService().isBackgroundRestricted(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the memory trim mode for a process and schedules a memory trim operation.
*
* <p><b>Note: this method is only intended for testing framework.</b></p>
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 8a8f044..c78255f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -27,7 +27,6 @@
import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.SparseIntArray;
-import android.view.RemoteAnimationAdapter;
import com.android.internal.app.IVoiceInteractor;
@@ -265,17 +264,6 @@
public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi);
/**
- * Sets if the given pid is currently running a remote animation, which is taken a signal for
- * determining oom adjustment and scheduling behavior.
- *
- * @param pid The pid we are setting overlay UI for.
- * @param runningRemoteAnimation True if the process is running a remote animation, false
- * otherwise.
- * @see RemoteAnimationAdapter
- */
- public abstract void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation);
-
- /**
* Called after the network policy rules are updated by
* {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and
* {@param procStateSeq}.
@@ -365,6 +353,11 @@
public abstract boolean isCallerRecents(int callingUid);
/**
+ * Returns whether the recents component is the home activity for the given user.
+ */
+ public abstract boolean isRecentsComponentHomeActivity(int userId);
+
+ /**
* Whether an UID is active or idle.
*/
public abstract boolean isUidActive(int uid);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 872370e..379944e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -143,7 +143,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.proto.MemInfoDumpProto;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.CloseGuard;
@@ -1251,55 +1251,62 @@
long parcelCount = Parcel.getGlobalAllocCount();
SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
- final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
- proto.write(MemInfoProto.ProcessMemory.PID, Process.myPid());
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME,
+ final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid());
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME,
(mBoundApplication != null) ? mBoundApplication.processName : "unknown");
dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly,
nativeMax, nativeAllocated, nativeFree,
dalvikMax, dalvikAllocated, dalvikFree);
proto.end(mToken);
- final long oToken = proto.start(MemInfoProto.AppData.OBJECTS);
- proto.write(MemInfoProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, viewInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
+ final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT,
+ viewInstanceCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
viewRootInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
appContextInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
activityInstanceCount);
- proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, globalAssetCount);
- proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT,
+ globalAssetCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
globalAssetManagerCount);
- proto.write(MemInfoProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
binderLocalObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
binderProxyObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_MEMORY_KB, parcelSize / 1024);
- proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
- proto.write(MemInfoProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB,
+ parcelSize / 1024);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
binderDeathObjectCount);
- proto.write(MemInfoProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, openSslSocketCount);
- proto.write(MemInfoProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT,
+ openSslSocketCount);
+ proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
webviewInstanceCount);
proto.end(oToken);
// SQLite mem info
- final long sToken = proto.start(MemInfoProto.AppData.SQL);
- proto.write(MemInfoProto.AppData.SqlStats.MEMORY_USED_KB, stats.memoryUsed / 1024);
- proto.write(MemInfoProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
+ final long sToken = proto.start(MemInfoDumpProto.AppData.SQL);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB,
+ stats.memoryUsed / 1024);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
stats.pageCacheOverflow / 1024);
- proto.write(MemInfoProto.AppData.SqlStats.MALLOC_SIZE_KB, stats.largestMemAlloc / 1024);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB,
+ stats.largestMemAlloc / 1024);
int n = stats.dbStats.size();
for (int i = 0; i < n; i++) {
DbStats dbStats = stats.dbStats.get(i);
- final long dToken = proto.start(MemInfoProto.AppData.SqlStats.DATABASES);
- proto.write(MemInfoProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
- proto.write(MemInfoProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
- proto.write(MemInfoProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
- proto.write(MemInfoProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside);
- proto.write(MemInfoProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
+ final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B,
+ dbStats.lookaside);
+ proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
proto.end(dToken);
}
proto.end(sToken);
@@ -1307,7 +1314,7 @@
// Asset details.
String assetAlloc = AssetManager.getAssetAllocations();
if (assetAlloc != null) {
- proto.write(MemInfoProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
+ proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
}
// Unreachable native memory
@@ -1315,7 +1322,7 @@
int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags;
boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
|| android.os.Build.IS_DEBUGGABLE;
- proto.write(MemInfoProto.AppData.UNREACHABLE_MEMORY,
+ proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY,
Debug.getUnreachableMemory(100, showContents));
}
}
@@ -2505,17 +2512,17 @@
boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) {
final long token = proto.start(fieldId);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.NAME, name);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.NAME, name);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
if (hasSwappedOutPss) {
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
} else {
- proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
+ proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
}
proto.end(token);
@@ -2530,26 +2537,26 @@
long dalvikMax, long dalvikAllocated, long dalvikFree) {
if (!dumpSummaryOnly) {
- final long nhToken = proto.start(MemInfoProto.ProcessMemory.NATIVE_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
+ final long nhToken = proto.start(MemInfoDumpProto.ProcessMemory.NATIVE_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
memInfo.nativePrivateClean, memInfo.hasSwappedOutPss,
memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
proto.end(nhToken);
- final long dvToken = proto.start(MemInfoProto.ProcessMemory.DALVIK_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
+ final long dvToken = proto.start(MemInfoDumpProto.ProcessMemory.DALVIK_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss,
memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
proto.end(dvToken);
int otherPss = memInfo.otherPss;
@@ -2573,7 +2580,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.OTHER_HEAPS,
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.OTHER_HEAPS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2590,21 +2597,23 @@
}
}
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
otherPss, otherSwappablePss,
otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss);
- final long tToken = proto.start(MemInfoProto.ProcessMemory.TOTAL_HEAP);
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
+ final long tToken = proto.start(MemInfoDumpProto.ProcessMemory.TOTAL_HEAP);
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
memInfo.getTotalPss(), memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(),
memInfo.getTotalSwappedOutPss());
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB,
+ nativeMax + dalvikMax);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
nativeAllocated + dalvikAllocated);
- proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
+ proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB,
+ nativeFree + dalvikFree);
proto.end(tToken);
if (dumpDalvik) {
@@ -2622,7 +2631,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.DALVIK_DETAILS,
+ dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.DALVIK_DETAILS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2632,24 +2641,26 @@
}
}
- final long asToken = proto.start(MemInfoProto.ProcessMemory.APP_SUMMARY);
- proto.write(MemInfoProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
+ final long asToken = proto.start(MemInfoDumpProto.ProcessMemory.APP_SUMMARY);
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
memInfo.getSummaryJavaHeap());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
memInfo.getSummaryNativeHeap());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_PSS_KB,
+ memInfo.getSummaryCode());
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_PSS_KB,
+ memInfo.getSummaryStack());
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
memInfo.getSummaryGraphics());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
memInfo.getSummaryPrivateOther());
- proto.write(MemInfoProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
memInfo.getSummarySystem());
if (memInfo.hasSwappedOutPss) {
- proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwapPss());
} else {
- proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwap());
}
proto.end(asToken);
@@ -3716,6 +3727,10 @@
if (localLOGV) Slog.v(TAG, "Performing resume of " + r
+ " finished=" + r.activity.mFinished);
if (r != null && !r.activity.mFinished) {
+ if (r.getLifecycleState() == ON_RESUME) {
+ throw new IllegalStateException(
+ "Trying to resume activity which is already resumed");
+ }
if (clearHide) {
r.hideForNow = false;
r.activity.mStartedActivity = false;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c5b3a4a..05a9861 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -31,7 +31,6 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -168,12 +167,14 @@
/** @hide */
public static final int OP_WRITE_SETTINGS = 23;
/** @hide Required to draw on top of other apps. */
+ @TestApi
public static final int OP_SYSTEM_ALERT_WINDOW = 24;
/** @hide */
public static final int OP_ACCESS_NOTIFICATIONS = 25;
/** @hide */
public static final int OP_CAMERA = 26;
/** @hide */
+ @TestApi
public static final int OP_RECORD_AUDIO = 27;
/** @hide */
public static final int OP_PLAY_AUDIO = 28;
@@ -1540,6 +1541,7 @@
*
* @hide
*/
+ @TestApi
public interface OnOpActiveChangedListener {
/**
* Called when the active state of an app op changes.
@@ -1731,15 +1733,14 @@
* Monitor for changes to the operating mode for the given op in the given app package.
*
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
- * to watch changes only for your UID.
+ * you can watch changes only for your UID.
*
* @param op The operation to monitor, one of OP_*.
* @param packageName The name of the application to monitor.
* @param callback Where to report changes.
* @hide
*/
- // TODO: Uncomment below annotation once b/73559440 is fixed
- // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) {
synchronized (mModeWatchers) {
IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -1788,6 +1789,9 @@
* watched ops for a registered callback you need to unregister and register it
* again.
*
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can watch changes only for your UID.
+ *
* @param ops The ops to watch.
* @param callback Where to report changes.
*
@@ -1798,7 +1802,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+ @TestApi
+ // TODO: Uncomment below annotation once b/73559440 is fixed
+ // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
public void startWatchingActive(@NonNull int[] ops,
@NonNull OnOpActiveChangedListener callback) {
Preconditions.checkNotNull(ops, "ops cannot be null");
@@ -1836,6 +1842,7 @@
*
* @hide
*/
+ @TestApi
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
synchronized (mActiveWatchers) {
final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
@@ -2087,15 +2094,11 @@
* @hide
*/
public int noteOp(int op, int uid, String packageName) {
- try {
- int mode = mService.noteOperation(op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ final int mode = noteOpNoThrow(op, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
+ return mode;
}
/**
@@ -2174,6 +2177,11 @@
}
}
+ /** @hide */
+ public int startOp(int op) {
+ return startOp(op, Process.myUid(), mContext.getOpPackageName());
+ }
+
/**
* Report that an application has started executing a long-running operation. Note that you
* must pass in both the uid and name of the application to be checked; this function will
@@ -2182,6 +2190,7 @@
* the current time and the operation will be marked as "running". In this case you must
* later call {@link #finishOp(int, int, String)} to report when the application is no
* longer performing the operation.
+ *
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -2192,15 +2201,34 @@
* @hide
*/
public int startOp(int op, int uid, String packageName) {
- try {
- int mode = mService.startOperation(getToken(mService), op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return startOp(op, uid, packageName, false);
+ }
+
+ /**
+ * Report that an application has started executing a long-running operation. Similar
+ * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT}
+ * the operation should succeed since the caller has performed its standard permission
+ * checks which passed and would perform the protected operation for this mode.
+ *
+ * @param op The operation to start. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+ *
+ * @throws SecurityException If the app has been configured to crash on this op or
+ * the package is not in the passed in UID.
+ *
+ * @hide
+ */
+ public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+ final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
+ return mode;
}
/**
@@ -2209,18 +2237,32 @@
* @hide
*/
public int startOpNoThrow(int op, int uid, String packageName) {
+ return startOpNoThrow(op, uid, packageName, false);
+ }
+
+ /**
+ * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a
+ * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ *
+ * @param op The operation to start. One of the OP_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+ *
+ * @hide
+ */
+ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
try {
- return mService.startOperation(getToken(mService), op, uid, packageName);
+ return mService.startOperation(getToken(mService), op, uid, packageName,
+ startIfModeDefault);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /** @hide */
- public int startOp(int op) {
- return startOp(op, Process.myUid(), mContext.getOpPackageName());
- }
-
/**
* Report that an application is no longer performing an operation that had previously
* been started with {@link #startOp(int, int, String)}. There is no validation of input
@@ -2241,8 +2283,21 @@
finishOp(op, Process.myUid(), mContext.getOpPackageName());
}
- /** @hide */
- @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+ /**
+ * Checks whether the given op for a UID and package is active.
+ *
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can query only for your UID.
+ *
+ * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+ * @see #stopWatchingMode(OnOpChangedListener)
+ * @see #finishOp(int)
+ * @see #startOp(int)
+ *
+ * @hide */
+ @TestApi
+ // TODO: Uncomment below annotation once b/73559440 is fixed
+ // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
public boolean isOperationActive(int code, int uid, String packageName) {
try {
return mService.isOperationActive(code, uid, packageName);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index ac301b3..eaa23c6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -614,7 +614,7 @@
int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
in String requiredPermission, in Bundle options);
-
+ boolean isBackgroundRestricted(in String packageName);
// Start of N MR1 transactions
void setVrThread(int tid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 233e09d..13a6be5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -360,6 +360,23 @@
@Deprecated
public RemoteViews headsUpContentView;
+ private boolean mUsesStandardHeader;
+
+ private static final ArraySet<Integer> STANDARD_LAYOUTS = new ArraySet<>();
+ static {
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_base);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_header);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
+ }
+
/**
* A large bitmap to be shown in the notification content area.
*
@@ -2534,6 +2551,8 @@
}
parcel.writeInt(mGroupAlertBehavior);
+
+ // mUsesStandardHeader is not written because it should be recomputed in listeners
}
/**
@@ -4092,6 +4111,25 @@
}
}
+ /**
+ * @hide
+ */
+ public boolean usesStandardHeader() {
+ if (mN.mUsesStandardHeader) {
+ return true;
+ }
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
+ if (mN.contentView == null && mN.bigContentView == null) {
+ return true;
+ }
+ }
+ boolean contentViewUsesHeader = mN.contentView == null
+ || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId());
+ boolean bigContentViewUsesHeader = mN.bigContentView == null
+ || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId());
+ return contentViewUsesHeader && bigContentViewUsesHeader;
+ }
+
private void resetStandardTemplate(RemoteViews contentView) {
resetNotificationHeader(contentView);
resetContentMargins(contentView);
@@ -4123,6 +4161,7 @@
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
+ mN.mUsesStandardHeader = false;
}
private void resetContentMargins(RemoteViews contentView) {
@@ -4444,6 +4483,7 @@
bindProfileBadge(contentView);
}
bindExpandButton(contentView);
+ mN.mUsesStandardHeader = true;
}
private void bindExpandButton(RemoteViews contentView) {
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index ee6a5ca..c2c91c2 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -16,7 +16,6 @@
package android.app;
import android.Manifest;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.IBinder;
@@ -76,6 +75,14 @@
}
/**
+ * Temporary. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
+ return addConfiguration(configKey, config);
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
@@ -219,11 +226,10 @@
* the retrieved metrics from statsd memory.
*
* @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
- * crashed).
+ * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
*/
@RequiresPermission(Manifest.permission.DUMP)
- public @Nullable byte[] getData(long configKey) {
+ public byte[] getData(long configKey) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
@@ -233,7 +239,7 @@
}
return service.getData(configKey);
} catch (RemoteException e) {
- if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
return null;
}
}
@@ -244,10 +250,10 @@
* the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
* This getter is not destructive and will not reset any metrics/counters.
*
- * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
+ * @return Serialized StatsdStatsReport proto. Returns null on failure.
*/
@RequiresPermission(Manifest.permission.DUMP)
- public @Nullable byte[] getMetadata() {
+ public byte[] getMetadata() {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1c3f34a..2c4bf82 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,8 +56,11 @@
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManager.UserOperationException;
+import android.os.UserManager.UserOperationResult;
import android.provider.ContactsContract.Directory;
import android.provider.Settings;
import android.security.AttestedKeyPair;
@@ -6572,6 +6575,9 @@
* <p>
* If the adminExtras are not null, they will be stored on the device until the user is started
* for the first time. Then the extras will be passed to the admin when onEnable is called.
+ * <p>From {@link android.os.Build.VERSION_CODES#P} onwards, if targeting
+ * {@link android.os.Build.VERSION_CODES#P}, throws {@link UserOperationException} instead of
+ * returning {@code null} on failure.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param name The user's name.
@@ -6586,6 +6592,9 @@
* @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
* user could not be created.
* @throws SecurityException if {@code admin} is not a device owner.
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#P} and running on
+ * {@link android.os.Build.VERSION_CODES#P}.
*/
public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
@NonNull String name,
@@ -6594,6 +6603,8 @@
throwIfParentInstance("createAndManageUser");
try {
return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
+ } catch (ServiceSpecificException e) {
+ throw new UserOperationException(e.getMessage(), e.errorCode);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -6637,77 +6648,15 @@
}
/**
- * Indicates user operation is successful.
- *
- * @see #startUserInBackground(ComponentName, UserHandle)
- * @see #stopUser(ComponentName, UserHandle)
- * @see #logoutUser(ComponentName)
- */
- public static final int USER_OPERATION_SUCCESS = 0;
-
- /**
- * Indicates user operation failed for unknown reason.
- *
- * @see #startUserInBackground(ComponentName, UserHandle)
- * @see #stopUser(ComponentName, UserHandle)
- * @see #logoutUser(ComponentName)
- */
- public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
-
- /**
- * Indicates user operation failed because target user is a managed profile.
- *
- * @see #startUserInBackground(ComponentName, UserHandle)
- * @see #stopUser(ComponentName, UserHandle)
- * @see #logoutUser(ComponentName)
- */
- public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
-
- /**
- * Indicates user operation failed because maximum running user limit has reached.
- *
- * @see #startUserInBackground(ComponentName, UserHandle)
- */
- public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
-
- /**
- * Indicates user operation failed because the target user is in foreground.
- *
- * @see #stopUser(ComponentName, UserHandle)
- * @see #logoutUser(ComponentName)
- */
- public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
-
- /**
- * Result returned from
- * <ul>
- * <li>{@link #startUserInBackground(ComponentName, UserHandle)}</li>
- * <li>{@link #stopUser(ComponentName, UserHandle)}</li>
- * <li>{@link #logoutUser(ComponentName)}</li>
- * </ul>
- *
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "USER_OPERATION_" }, value = {
- USER_OPERATION_SUCCESS,
- USER_OPERATION_ERROR_UNKNOWN,
- USER_OPERATION_ERROR_MANAGED_PROFILE,
- USER_OPERATION_ERROR_MAX_RUNNING_USERS,
- USER_OPERATION_ERROR_CURRENT_USER
- })
- public @interface UserOperationResult {}
-
- /**
* Called by a device owner to start the specified secondary user in background.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle the user to be started in background.
* @return one of the following result codes:
- * {@link #USER_OPERATION_ERROR_UNKNOWN},
- * {@link #USER_OPERATION_SUCCESS},
- * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
- * {@link #USER_OPERATION_ERROR_MAX_RUNNING_USERS},
+ * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+ * {@link UserManager#USER_OPERATION_SUCCESS},
+ * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+ * {@link UserManager#USER_OPERATION_ERROR_MAX_RUNNING_USERS},
* @throws SecurityException if {@code admin} is not a device owner.
* @see #getSecondaryUsers(ComponentName)
*/
@@ -6727,10 +6676,10 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle the user to be stopped.
* @return one of the following result codes:
- * {@link #USER_OPERATION_ERROR_UNKNOWN},
- * {@link #USER_OPERATION_SUCCESS},
- * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
- * {@link #USER_OPERATION_ERROR_CURRENT_USER}
+ * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+ * {@link UserManager#USER_OPERATION_SUCCESS},
+ * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+ * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
* @throws SecurityException if {@code admin} is not a device owner.
* @see #getSecondaryUsers(ComponentName)
*/
@@ -6750,10 +6699,10 @@
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return one of the following result codes:
- * {@link #USER_OPERATION_ERROR_UNKNOWN},
- * {@link #USER_OPERATION_SUCCESS},
- * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
- * {@link #USER_OPERATION_ERROR_CURRENT_USER}
+ * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+ * {@link UserManager#USER_OPERATION_SUCCESS},
+ * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+ * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
* @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
* @see #getSecondaryUsers(ComponentName)
*/
@@ -9443,6 +9392,11 @@
/**
* Called by device owner to add an override APN.
*
+ * <p>This method may returns {@code -1} if {@code apnSetting} conflicts with an existing
+ * override APN. Update the existing conflicted APN with
+ * {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry.
+ * <p>See {@link ApnSetting} for the definition of conflict.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnSetting the override APN to insert
* @return The {@code id} of inserted override APN. Or {@code -1} when failed to insert into
@@ -9466,6 +9420,12 @@
/**
* Called by device owner to update an override APN.
*
+ * <p>This method may returns {@code false} if there is no override APN with the given
+ * {@code apnId}.
+ * <p>This method may also returns {@code false} if {@code apnSetting} conflicts with an
+ * existing override APN. Update the existing conflicted APN instead.
+ * <p>See {@link ApnSetting} for the definition of conflict.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnId the {@code id} of the override APN to update
* @param apnSetting the override APN to update
@@ -9491,6 +9451,9 @@
/**
* Called by device owner to remove an override APN.
*
+ * <p>This method may returns {@code false} if there is no override APN with the given
+ * {@code apnId}.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnId the {@code id} of the override APN to remove
* @return {@code true} if the required override APN is successfully removed, {@code false}
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 73b5ec4..545463c 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,7 +16,7 @@
package android.app.servertransaction;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.app.ClientTransactionHandler;
@@ -38,8 +38,8 @@
private List<ResultInfo> mResultInfoList;
@Override
- public int getPreExecutionState() {
- return ON_PAUSE;
+ public int getPostExecutionState() {
+ return ON_RESUME;
}
@Override
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index 6f2cc00..d94f08b 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -32,12 +32,6 @@
*/
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
- /** Get the state in which this callback can be executed. */
- @LifecycleState
- public int getPreExecutionState() {
- return UNDEFINED;
- }
-
/** Get the state that must follow this callback. */
@LifecycleState
public int getPostExecutionState() {
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 7dfde73..e5ce3b0 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -38,11 +38,6 @@
// TODO(lifecycler): Switch new intent handling to this scheme.
/*@Override
- public int getPreExecutionState() {
- return ON_PAUSE;
- }
-
- @Override
public int getPostExecutionState() {
return ON_RESUME;
}*/
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 059e0af..0e52b34 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -24,6 +24,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -48,11 +49,7 @@
private ClientTransactionHandler mTransactionHandler;
private PendingTransactionActions mPendingActions = new PendingTransactionActions();
-
- // Temp holder for lifecycle path.
- // No direct transition between two states should take more than one complete cycle of 6 states.
- @ActivityLifecycleItem.LifecycleState
- private IntArray mLifecycleSequence = new IntArray(6);
+ private TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
@@ -89,13 +86,25 @@
final IBinder token = transaction.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
+ // In case when post-execution state of the last callback matches the final state requested
+ // for the activity in this transaction, we won't do the last transition here and do it when
+ // moving to final state instead (because it may contain additional parameters from server).
+ final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
+ final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
+ : UNDEFINED;
+ // Index of the last callback that requests some post-execution state.
+ final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
+
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
log("Resolving callback: " + item);
- final int preExecutionState = item.getPreExecutionState();
- if (preExecutionState != UNDEFINED) {
- cycleToPath(r, preExecutionState);
+ final int postExecutionState = item.getPostExecutionState();
+ final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
+ item.getPostExecutionState());
+ if (closestPreExecutionState != UNDEFINED) {
+ cycleToPath(r, closestPreExecutionState);
}
item.execute(mTransactionHandler, token, mPendingActions);
@@ -105,9 +114,11 @@
r = mTransactionHandler.getActivityClient(token);
}
- final int postExecutionState = item.getPostExecutionState();
- if (postExecutionState != UNDEFINED) {
- cycleToPath(r, postExecutionState);
+ if (postExecutionState != UNDEFINED && r != null) {
+ // Skip the very last transition and perform it by explicit state request instead.
+ final boolean shouldExcludeLastTransition =
+ i == lastCallbackRequestingState && finalState == postExecutionState;
+ cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
}
}
}
@@ -162,15 +173,15 @@
boolean excludeLastState) {
final int start = r.getLifecycleState();
log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
- initLifecyclePath(start, finish, excludeLastState);
- performLifecycleSequence(r);
+ final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
+ performLifecycleSequence(r, path);
}
/** Transition the client through previously initialized state sequence. */
- private void performLifecycleSequence(ActivityClientRecord r) {
- final int size = mLifecycleSequence.size();
+ private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
+ final int size = path.size();
for (int i = 0, state; i < size; i++) {
- state = mLifecycleSequence.get(i);
+ state = path.get(i);
log("Transitioning to state: " + state);
switch (state) {
case ON_CREATE:
@@ -195,8 +206,7 @@
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
- "performLifecycleSequence. cycling to:"
- + mLifecycleSequence.get(size - 1));
+ "performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
@@ -207,60 +217,6 @@
}
}
- /**
- * Calculate the path through main lifecycle states for an activity and fill
- * @link #mLifecycleSequence} with values starting with the state that follows the initial
- * state.
- */
- public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
- mLifecycleSequence.clear();
- if (finish >= start) {
- // just go there
- for (int i = start + 1; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- } else { // finish < start, can't just cycle down
- if (start == ON_PAUSE && finish == ON_RESUME) {
- // Special case when we can just directly go to resumed state.
- mLifecycleSequence.add(ON_RESUME);
- } else if (start <= ON_STOP && finish >= ON_START) {
- // Restart and go to required state.
-
- // Go to stopped state first.
- for (int i = start + 1; i <= ON_STOP; i++) {
- mLifecycleSequence.add(i);
- }
- // Restart
- mLifecycleSequence.add(ON_RESTART);
- // Go to required state
- for (int i = ON_START; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- } else {
- // Relaunch and go to required state
-
- // Go to destroyed state first.
- for (int i = start + 1; i <= ON_DESTROY; i++) {
- mLifecycleSequence.add(i);
- }
- // Go to required state
- for (int i = ON_CREATE; i <= finish; i++) {
- mLifecycleSequence.add(i);
- }
- }
- }
-
- // Remove last transition in case we want to perform it with some specific params.
- if (excludeLastState && mLifecycleSequence.size() != 0) {
- mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
- }
- }
-
- @VisibleForTesting
- public int[] getLifecycleSequence() {
- return mLifecycleSequence.toArray();
- }
-
private static void log(String message) {
if (DEBUG_RESOLVER) Slog.d(TAG, message);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
new file mode 100644
index 0000000..7e66fd7
--- /dev/null
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.app.ActivityThread;
+import android.util.IntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution.
+ * @hide
+ */
+public class TransactionExecutorHelper {
+ // A penalty applied to path with destruction when looking for the shortest one.
+ private static final int DESTRUCTION_PENALTY = 10;
+
+ private static final int[] ON_RESUME_PRE_EXCUTION_STATES = new int[] { ON_START, ON_PAUSE };
+
+ // Temp holder for lifecycle path.
+ // No direct transition between two states should take more than one complete cycle of 6 states.
+ @ActivityLifecycleItem.LifecycleState
+ private IntArray mLifecycleSequence = new IntArray(6);
+
+ /**
+ * Calculate the path through main lifecycle states for an activity and fill
+ * @link #mLifecycleSequence} with values starting with the state that follows the initial
+ * state.
+ * <p>NOTE: The returned value is used internally in this class and is not a copy. It's contents
+ * may change after calling other methods of this class.</p>
+ */
+ @VisibleForTesting
+ public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
+ if (start == UNDEFINED || finish == UNDEFINED) {
+ throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
+ }
+ if (start == ON_RESTART || finish == ON_RESTART) {
+ throw new IllegalArgumentException(
+ "Can't start or finish in intermittent RESTART state");
+ }
+ if (finish == PRE_ON_CREATE && start != finish) {
+ throw new IllegalArgumentException("Can only start in pre-onCreate state");
+ }
+
+ mLifecycleSequence.clear();
+ if (finish >= start) {
+ // just go there
+ for (int i = start + 1; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ } else { // finish < start, can't just cycle down
+ if (start == ON_PAUSE && finish == ON_RESUME) {
+ // Special case when we can just directly go to resumed state.
+ mLifecycleSequence.add(ON_RESUME);
+ } else if (start <= ON_STOP && finish >= ON_START) {
+ // Restart and go to required state.
+
+ // Go to stopped state first.
+ for (int i = start + 1; i <= ON_STOP; i++) {
+ mLifecycleSequence.add(i);
+ }
+ // Restart
+ mLifecycleSequence.add(ON_RESTART);
+ // Go to required state
+ for (int i = ON_START; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ } else {
+ // Relaunch and go to required state
+
+ // Go to destroyed state first.
+ for (int i = start + 1; i <= ON_DESTROY; i++) {
+ mLifecycleSequence.add(i);
+ }
+ // Go to required state
+ for (int i = ON_CREATE; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
+ }
+ }
+
+ // Remove last transition in case we want to perform it with some specific params.
+ if (excludeLastState && mLifecycleSequence.size() != 0) {
+ mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
+ }
+
+ return mLifecycleSequence;
+ }
+
+ /**
+ * Pick a state that goes before provided post-execution state and would require the least
+ * lifecycle transitions to get to.
+ * It will also make sure to try avoiding a path with activity destruction and relaunch if
+ * possible.
+ * @param r An activity that we're trying to resolve the transition for.
+ * @param postExecutionState Post execution state to compute for.
+ * @return One of states that precede the provided post-execution state, or
+ * {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
+ */
+ @VisibleForTesting
+ public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+ int postExecutionState) {
+ switch (postExecutionState) {
+ case UNDEFINED:
+ return UNDEFINED;
+ case ON_RESUME:
+ return getClosestOfStates(r, ON_RESUME_PRE_EXCUTION_STATES);
+ default:
+ throw new UnsupportedOperationException("Pre-execution states for state: "
+ + postExecutionState + " is not supported.");
+ }
+ }
+
+ /**
+ * Pick a state that would require the least lifecycle transitions to get to.
+ * It will also make sure to try avoiding a path with activity destruction and relaunch if
+ * possible.
+ * @param r An activity that we're trying to resolve the transition for.
+ * @param finalStates An array of valid final states.
+ * @return One of the provided final states, or {@link ActivityLifecycleItem#UNDEFINED} if none
+ * were provided or there is not path.
+ */
+ @VisibleForTesting
+ public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+ if (finalStates == null || finalStates.length == 0) {
+ return UNDEFINED;
+ }
+
+ final int currentState = r.getLifecycleState();
+ int closestState = UNDEFINED;
+ for (int i = 0, shortestPath = Integer.MAX_VALUE, pathLength; i < finalStates.length; i++) {
+ getLifecyclePath(currentState, finalStates[i], false /* excludeLastState */);
+ pathLength = mLifecycleSequence.size();
+ if (pathInvolvesDestruction(mLifecycleSequence)) {
+ pathLength += DESTRUCTION_PENALTY;
+ }
+ if (shortestPath > pathLength) {
+ shortestPath = pathLength;
+ closestState = finalStates[i];
+ }
+ }
+ return closestState;
+ }
+
+ /**
+ * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
+ * that involves destruction and recreation if there is another path.
+ */
+ private static boolean pathInvolvesDestruction(IntArray lifecycleSequence) {
+ final int size = lifecycleSequence.size();
+ for (int i = 0; i < size; i++) {
+ if (lifecycleSequence.get(i) == ON_DESTROY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the index of the last callback that requests the state in which activity will be after
+ * execution. If there is a group of callbacks in the end that requests the same specific state
+ * or doesn't request any - we will find the first one from such group.
+ *
+ * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
+ * specific state. If there is a sequence
+ * Configuration - ActivityResult - Configuration - ActivityResult
+ * index 1 will be returned, because ActivityResult request on position 1 will be the last
+ * request that moves activity to the RESUMED state where it will eventually end.
+ */
+ static int lastCallbackRequestingState(ClientTransaction transaction) {
+ final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
+ if (callbacks == null || callbacks.size() == 0) {
+ return -1;
+ }
+
+ // Go from the back of the list to front, look for the request closes to the beginning that
+ // requests the state in which activity will end after all callbacks are executed.
+ int lastRequestedState = UNDEFINED;
+ int lastRequestingCallback = -1;
+ for (int i = callbacks.size() - 1; i >= 0; i--) {
+ final ClientTransactionItem callback = callbacks.get(i);
+ final int postExecutionState = callback.getPostExecutionState();
+ if (postExecutionState != UNDEFINED) {
+ // Found a callback that requests some post-execution state.
+ if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) {
+ // It's either a first-from-end callback that requests state or it requests
+ // the same state as the last one. In both cases, we will use it as the new
+ // candidate.
+ lastRequestedState = postExecutionState;
+ lastRequestingCallback = i;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return lastRequestingCallback;
+ }
+}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 0a5795e..b5c69d8 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -436,7 +436,7 @@
}
/**
- * Add a color to the slice being constructed
+ * Add an integer to the slice being constructed
* @param subType Optional template-specific type information
* @see {@link SliceItem#getSubType()}
*/
@@ -446,7 +446,7 @@
}
/**
- * Add a color to the slice being constructed
+ * Add an integer to the slice being constructed
* @param subType Optional template-specific type information
* @see {@link SliceItem#getSubType()}
*/
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index a957aed..cec3bad 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1872,9 +1872,10 @@
du.println(sb.toString());
}
}
- if (mPriority != 0 || mHasPartialTypes) {
+ if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) {
sb.setLength(0);
sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
+ sb.append(", mOrder="); sb.append(mOrder);
sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
du.println(sb.toString());
}
@@ -1951,6 +1952,7 @@
dest.writeInt(mHasPartialTypes ? 1 : 0);
dest.writeInt(getAutoVerify() ? 1 : 0);
dest.writeInt(mInstantAppVisibility);
+ dest.writeInt(mOrder);
}
/**
@@ -2020,6 +2022,7 @@
mHasPartialTypes = source.readInt() > 0;
setAutoVerify(source.readInt() > 0);
setVisibilityToInstantApp(source.readInt());
+ mOrder = source.readInt();
}
private final boolean findMimeType(String type) {
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 41aa9c3..514112f 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -236,6 +236,11 @@
int userId);
/**
+ * @return The default home activity component name.
+ */
+ public abstract ComponentName getDefaultHomeActivity(int userId);
+
+ /**
* Called by DeviceOwnerManagerService to set the package names of device owner and profile
* owners.
*/
@@ -539,4 +544,16 @@
/** Updates the flags for the given permission. */
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSigHash.
+ */
+ public abstract boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSig.
+ */
+ public abstract boolean isDataRestoreSafe(Signature restoringFromSig, String packageName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2420b63..5f10da1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3190,7 +3190,7 @@
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
- outError[0] = "<permission> protectionLevel specifies a non-instnat flag but is "
+ outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -3641,7 +3641,9 @@
// getting added to the wrong package.
final CachedComponentArgs cachedArgs = new CachedComponentArgs();
int type;
-
+ boolean hasActivityOrder = false;
+ boolean hasReceiverOrder = false;
+ boolean hasServiceOrder = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3657,6 +3659,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
@@ -3667,6 +3670,7 @@
return false;
}
+ hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
@@ -3676,6 +3680,7 @@
return false;
}
+ hasServiceOrder |= (s.order != 0);
owner.services.add(s);
} else if (tagName.equals("provider")) {
@@ -3694,6 +3699,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
@@ -3827,6 +3833,15 @@
}
}
+ if (hasActivityOrder) {
+ Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+ }
+ if (hasReceiverOrder) {
+ Collections.sort(owner.receivers, (r1, r2) -> Integer.compare(r2.order, r1.order));
+ }
+ if (hasServiceOrder) {
+ Collections.sort(owner.services, (s1, s2) -> Integer.compare(s2.order, s1.order));
+ }
// Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(owner);
@@ -4368,6 +4383,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4745,6 +4761,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4952,6 +4969,7 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -5241,6 +5259,7 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ s.order = Math.max(intent.getOrder(), s.order);
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -5466,6 +5485,10 @@
com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
outInfo.setPriority(priority);
+ int order = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+ outInfo.setOrder(order);
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -7053,6 +7076,8 @@
public Bundle metaData;
public Package owner;
+ /** The order of this component in relation to its peers */
+ public int order;
ComponentName componentName;
String componentShortName;
@@ -7571,6 +7596,7 @@
for (ActivityIntentInfo aii : intents) {
aii.activity = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
@@ -7660,6 +7686,7 @@
for (ServiceIntentInfo aii : intents) {
aii.service = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 2baf539..19b5c45 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -16,10 +16,11 @@
package android.content.res;
+import static android.content.ConfigurationProto.COLOR_MODE;
import static android.content.ConfigurationProto.DENSITY_DPI;
import static android.content.ConfigurationProto.FONT_SCALE;
import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
-import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD;
import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.LOCALES;
import static android.content.ConfigurationProto.MCC;
@@ -33,7 +34,6 @@
import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
import static android.content.ConfigurationProto.TOUCHSCREEN;
import static android.content.ConfigurationProto.UI_MODE;
-import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
import static android.content.ResourcesConfigurationProto.CONFIGURATION;
import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
@@ -1095,11 +1095,9 @@
protoOutputStream.write(MNC, mnc);
mLocaleList.writeToProto(protoOutputStream, LOCALES);
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
- protoOutputStream.write(HDR_COLOR_MODE,
- (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
- protoOutputStream.write(WIDE_COLOR_GAMUT,
- colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+ protoOutputStream.write(COLOR_MODE, colorMode);
protoOutputStream.write(TOUCHSCREEN, touchscreen);
+ protoOutputStream.write(KEYBOARD, keyboard);
protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
protoOutputStream.write(NAVIGATION, navigation);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6b2059e..36d5615 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -615,6 +615,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public Point getStableDisplaySize() {
return mGlobal.getStableDisplaySize();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index bae373d..9d94ecc 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -117,6 +117,7 @@
NET_CAPABILITY_FOREGROUND,
NET_CAPABILITY_NOT_CONGESTED,
NET_CAPABILITY_NOT_SUSPENDED,
+ NET_CAPABILITY_OEM_PAID,
})
public @interface NetCapability { }
@@ -264,8 +265,15 @@
*/
public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+ /**
+ * Indicates that traffic that goes through this network is paid by oem. For example,
+ * this network can be used by system apps to upload telemetry data.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_OEM_PAID = 22;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PAID;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -313,7 +321,8 @@
(1 << NET_CAPABILITY_IA) |
(1 << NET_CAPABILITY_IMS) |
(1 << NET_CAPABILITY_RCS) |
- (1 << NET_CAPABILITY_XCAP);
+ (1 << NET_CAPABILITY_XCAP) |
+ (1 << NET_CAPABILITY_OEM_PAID);
/**
* Capabilities that suggest that a network is unrestricted.
@@ -1313,6 +1322,7 @@
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
+ case NET_CAPABILITY_OEM_PAID: return "OEM_PAID";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 48f5684..6d8831b 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
@@ -287,6 +288,7 @@
* we are operating under, we bump the assumed resource platform version by 1.
* @hide
*/
+ @TestApi
public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 811cc5e..97d415e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -538,6 +538,7 @@
ServiceType.DATA_SAVER,
ServiceType.FORCE_ALL_APPS_STANDBY,
ServiceType.OPTIONAL_SENSORS,
+ ServiceType.AOD,
})
public @interface ServiceType {
int NULL = 0;
@@ -551,6 +552,7 @@
int SOUND = 8;
int BATTERY_STATS = 9;
int DATA_SAVER = 10;
+ int AOD = 14;
/**
* Whether to enable force-app-standby on all apps or not.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 41cc9f8..2693bab 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1042,6 +1042,85 @@
*/
public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;
+ /**
+ * Indicates user operation is successful.
+ */
+ public static final int USER_OPERATION_SUCCESS = 0;
+
+ /**
+ * Indicates user operation failed for unknown reason.
+ */
+ public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
+
+ /**
+ * Indicates user operation failed because target user is a managed profile.
+ */
+ public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
+
+ /**
+ * Indicates user operation failed because maximum running user limit has been reached.
+ */
+ public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
+
+ /**
+ * Indicates user operation failed because the target user is in the foreground.
+ */
+ public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
+
+ /**
+ * Indicates user operation failed because device has low data storage.
+ */
+ public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5;
+
+ /**
+ * Indicates user operation failed because maximum user limit has been reached.
+ */
+ public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
+
+ /**
+ * Result returned from various user operations.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "USER_OPERATION_" }, value = {
+ USER_OPERATION_SUCCESS,
+ USER_OPERATION_ERROR_UNKNOWN,
+ USER_OPERATION_ERROR_MANAGED_PROFILE,
+ USER_OPERATION_ERROR_MAX_RUNNING_USERS,
+ USER_OPERATION_ERROR_CURRENT_USER,
+ USER_OPERATION_ERROR_LOW_STORAGE,
+ USER_OPERATION_ERROR_MAX_USERS
+ })
+ public @interface UserOperationResult {}
+
+ /**
+ * Thrown to indicate user operation failed.
+ */
+ public static class UserOperationException extends RuntimeException {
+ private final @UserOperationResult int mUserOperationResult;
+
+ /**
+ * Constructs a UserOperationException with specific result code.
+ *
+ * @param message the detail message
+ * @param userOperationResult the result code
+ * @hide
+ */
+ public UserOperationException(String message,
+ @UserOperationResult int userOperationResult) {
+ super(message);
+ mUserOperationResult = userOperationResult;
+ }
+
+ /**
+ * Returns the operation result code.
+ */
+ public @UserOperationResult int getUserOperationResult() {
+ return mUserOperationResult;
+ }
+ }
+
/** @hide */
public static UserManager get(Context context) {
return (UserManager) context.getSystemService(Context.USER_SERVICE);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0299407..781a5f8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10516,8 +10516,11 @@
* entity_list_default use ":" as delimiter for values. Ex:
*
* <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
+ * model_dark_launch_enabled (boolean)
+ * smart_selection_enabled (boolean)
+ * smart_text_share_enabled (boolean)
+ * smart_linkify_enabled (boolean)
+ * smart_select_animation_enabled (boolean)
* suggest_selection_max_range_length (int)
* classify_text_max_range_length (int)
* generate_links_max_text_length (int)
@@ -10530,7 +10533,7 @@
* <p>
* Type: string
* @hide
- * see also android.view.textclassifier.TextClassifierConstants
+ * see also android.view.textclassifier.TextClassificationConstants
*/
public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
@@ -10590,6 +10593,23 @@
= "forced_app_standby_for_small_battery_enabled";
/**
+ * Whether or not to enable the Off Body, Radios Off feature on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
+ = "off_body_radios_off_for_small_battery_enabled";
+
+ /**
+ * How long after the device goes off body to disable radios, in milliseconds.
+ * Type: long
+ * Default: 10 minutes
+ * @hide
+ */
+ public static final String OFF_BODY_RADIOS_OFF_DELAY_MS = "off_body_radios_off_delay_ms";
+
+ /**
* Whether or not to enable Time Only Mode for watch type devices.
* Type: int (0 for false, 1 for true)
* Default: 0
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index f043d6a..4580c47 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -116,7 +116,6 @@
* See implementation for binary key format.
*
* @deprecated Use {@link #getTrustedHardwareCertPath} instead.
- * @removed
*/
@Deprecated
public @NonNull byte[] getTrustedHardwarePublicKey() {
@@ -221,7 +220,6 @@
* @param publicKey The public key
* @return This builder.
* @deprecated Use {@link #setTrustedHardwareCertPath} instead.
- * @removed
*/
@Deprecated
public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 426ca5c..6c882e1 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -184,7 +184,6 @@
/**
* @deprecated Use {@link #getKeyChainSnapshot()}
- * @removed
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -262,7 +261,6 @@
/**
* @deprecated Use {@link #getAliases()}.
- * @removed
*/
@Deprecated
public List<String> getAliases(@Nullable String packageName)
@@ -286,7 +284,6 @@
/**
* @deprecated Use {@link #setRecoveryStatus(String, int)}
- * @removed
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -321,7 +318,6 @@
/**
* @deprecated Use {@link #getRecoveryStatus(String)}.
- * @removed
*/
@Deprecated
public int getRecoveryStatus(String packageName, String alias)
@@ -472,7 +468,6 @@
// TODO: Unhide the following APIs, generateKey(), importKey(), and getKey()
/**
* @deprecated Use {@link #generateKey(String)}.
- * @removed
*/
@Deprecated
public Key generateKey(@NonNull String alias, byte[] account)
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index e42c766..2b627b4 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -73,7 +73,6 @@
/**
* @deprecated Use {@link #start(CertPath, byte[], byte[], List)} instead.
- * @removed
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index df9766d..714e35a 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -61,7 +61,6 @@
/**
* @deprecated AOSP does not associate keys with accounts. This may be done by system app.
- * @removed
*/
@Deprecated
public Builder setAccount(@NonNull byte[] account) {
@@ -120,7 +119,6 @@
/**
* @deprecated AOSP does not associate keys with accounts. This may be done by system app.
- * @removed
*/
@Deprecated
public @NonNull byte[] getAccount() {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1ead0b4..a5a7cbc 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,7 +37,6 @@
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 517b13b..3350f3e 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -18,7 +18,8 @@
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics inside statsd.
+ * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
+ * statsd.
*/
public final class StatsLog extends StatsLogInternal {
private static final String TAG = "StatsManager";
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index fbba8ab..137e820 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
@@ -25,6 +26,7 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -53,6 +55,10 @@
private ImageView mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
+ private View mOverlayIcon;
+ private View mCameraIcon;
+ private View mMicIcon;
+ private View mAppOps;
private int mIconColor;
private int mOriginalNotificationColor;
private boolean mExpanded;
@@ -108,6 +114,10 @@
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+ mCameraIcon = findViewById(com.android.internal.R.id.camera);
+ mMicIcon = findViewById(com.android.internal.R.id.mic);
+ mOverlayIcon = findViewById(com.android.internal.R.id.overlay);
+ mAppOps = findViewById(com.android.internal.R.id.app_ops);
}
@Override
@@ -198,6 +208,11 @@
layoutRight = end - paddingEnd;
end = layoutLeft = layoutRight - child.getMeasuredWidth();
}
+ if (child == mAppOps) {
+ int paddingEnd = mContentEndMargin;
+ layoutRight = end - paddingEnd;
+ end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ }
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
int ltrLeft = layoutLeft;
layoutLeft = getWidth() - layoutRight;
@@ -289,6 +304,22 @@
updateExpandButton();
}
+ /**
+ * Shows or hides 'app op in use' icons based on app usage.
+ */
+ public void showAppOpsIcons(ArraySet<Integer> appOps) {
+ if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null) {
+ return;
+ }
+
+ mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ ? View.VISIBLE : View.GONE);
+ mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
+ ? View.VISIBLE : View.GONE);
+ mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
+ ? View.VISIBLE : View.GONE);
+ }
+
private void updateExpandButton() {
int drawableId;
int contentDescriptionId;
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index a864e55..d597e59 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -52,9 +52,6 @@
private final long mDuration;
private final long mStatusBarTransitionDelay;
- /** @see #getCallingPid */
- private int mCallingPid;
-
/**
* @param runner The interface that gets notified when we actually need to start the animation.
* @param duration The duration of the animation.
@@ -86,20 +83,6 @@
return mStatusBarTransitionDelay;
}
- /**
- * To be called by system_server to keep track which pid is running this animation.
- */
- public void setCallingPid(int pid) {
- mCallingPid = pid;
- }
-
- /**
- * @return The pid of the process running the animation.
- */
- public int getCallingPid() {
- return mCallingPid;
- }
-
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java
index 8def435..381f692 100644
--- a/core/java/android/view/RemoteAnimationDefinition.java
+++ b/core/java/android/view/RemoteAnimationDefinition.java
@@ -70,16 +70,6 @@
mTransitionAnimationMap = in.readSparseArray(null /* loader */);
}
- /**
- * To be called by system_server to keep track which pid is running the remote animations inside
- * this definition.
- */
- public void setCallingPid(int pid) {
- for (int i = mTransitionAnimationMap.size() - 1; i >= 0; i--) {
- mTransitionAnimationMap.valueAt(i).setCallingPid(pid);
- }
- }
-
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bd7f8e5..cc9b8a8 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -763,18 +763,14 @@
}
public void deferTransactionUntil(IBinder handle, long frame) {
- if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntil(this, handle, frame);
- }
+ synchronized(SurfaceControl.class) {
+ sGlobalTransaction.deferTransactionUntil(this, handle, frame);
}
}
public void deferTransactionUntil(Surface barrier, long frame) {
- if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
- }
+ synchronized(SurfaceControl.class) {
+ sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
}
}
@@ -1479,6 +1475,9 @@
public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
long frameNumber) {
+ if (frameNumber < 0) {
+ return this;
+ }
sc.checkNotReleased();
nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
return this;
@@ -1486,6 +1485,9 @@
public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
long frameNumber) {
+ if (frameNumber < 0) {
+ return this;
+ }
sc.checkNotReleased();
nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
barrierSurface.mNativeObject, frameNumber);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f61b652..e285222 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2953,6 +2953,9 @@
* 1 PFLAG3_NO_REVEAL_ON_FOCUS
* 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT
* 1 PFLAG3_SCREEN_READER_FOCUSABLE
+ * 1 PFLAG3_AGGREGATED_VISIBLE
+ * 1 PFLAG3_AUTOFILLID_EXPLICITLY_SET
+ * 1 available
* |-------|-------|-------|-------|
*/
@@ -3243,6 +3246,12 @@
*/
private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
+ /**
+ * Used to indicate that {@link #mAutofillId} was explicitly set through
+ * {@link #setAutofillId(AutofillId)}.
+ */
+ private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -8205,16 +8214,28 @@
* @throws IllegalArgumentException if the id is an autofill id associated with a virtual view.
*/
public void setAutofillId(@Nullable AutofillId id) {
+ // TODO(b/37566627): add unit / CTS test for all possible combinations below
if (android.view.autofill.Helper.sVerbose) {
Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
}
if (isAttachedToWindow()) {
throw new IllegalStateException("Cannot set autofill id when view is attached");
}
- if (id.isVirtual()) {
+ if (id != null && id.isVirtual()) {
throw new IllegalStateException("Cannot set autofill id assigned to virtual views");
}
+ if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) {
+ // Ignore reset because it was never explicitly set before.
+ return;
+ }
mAutofillId = id;
+ if (id != null) {
+ mAutofillViewId = id.getViewId();
+ mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+ } else {
+ mAutofillViewId = NO_ID;
+ mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+ }
}
/**
@@ -18524,7 +18545,17 @@
// Hence prevent the same autofill view id from being restored multiple times.
((BaseSavedState) state).mSavedData &= ~BaseSavedState.AUTOFILL_ID;
- mAutofillViewId = baseState.mAutofillViewId;
+ if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) {
+ // Ignore when view already set it through setAutofillId();
+ if (android.view.autofill.Helper.sDebug) {
+ Log.d(VIEW_LOG_TAG, "onRestoreInstanceState(): not setting autofillId to "
+ + baseState.mAutofillViewId + " because view explicitly set it to "
+ + mAutofillId);
+ }
+ } else {
+ mAutofillViewId = baseState.mAutofillViewId;
+ mAutofillId = null; // will be set on demand by getAutofillId()
+ }
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 01d9265..95e4abb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -95,6 +95,7 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
+import android.view.autofill.AutofillManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -4781,6 +4782,21 @@
ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
}
+ if (action == MotionEvent.ACTION_DOWN && mView instanceof ViewGroup) {
+ // Upon motion event within app window, close autofill ui.
+ ViewGroup decorView = (ViewGroup) mView;
+ if (decorView.getChildCount() > 0) {
+ // We cannot use decorView's Context for querying AutofillManager: DecorView's
+ // context is based on Application Context, it would allocate a different
+ // AutofillManager instance.
+ AutofillManager afm = (AutofillManager) decorView.getChildAt(0).getContext()
+ .getSystemService(Context.AUTOFILL_MANAGER_SERVICE);
+ if (afm != null) {
+ afm.requestHideFillUi();
+ }
+ }
+ }
+
if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
@@ -6440,18 +6456,24 @@
params.backup();
mTranslator.translateWindowLayout(params);
}
+
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
- }
- if (params != null && mOrigWindowType != params.type) {
- // For compatibility with old apps, don't crash here.
- if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Slog.w(mTag, "Window type can not be changed after "
- + "the window is added; ignoring change of " + mView);
- params.type = mOrigWindowType;
+ if (mOrigWindowType != params.type) {
+ // For compatibility with old apps, don't crash here.
+ if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ Slog.w(mTag, "Window type can not be changed after "
+ + "the window is added; ignoring change of " + mView);
+ params.type = mOrigWindowType;
+ }
+ }
+
+ if (mSurface.isValid()) {
+ params.frameNumber = mSurface.getNextFrameNumber();
}
}
+
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c0a9666..e271701 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2371,6 +2371,13 @@
public long hideTimeoutMilliseconds = -1;
/**
+ * A frame number in which changes requested in this layout will be rendered.
+ *
+ * @hide
+ */
+ public long frameNumber = -1;
+
+ /**
* The color mode requested by this window. The target display may
* not be able to honor the request. When the color mode is not set
* to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2543,6 +2550,7 @@
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
out.writeLong(hideTimeoutMilliseconds);
+ out.writeLong(frameNumber);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2599,6 +2607,7 @@
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
hideTimeoutMilliseconds = in.readLong();
+ frameNumber = in.readLong();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2799,6 +2808,10 @@
changes |= SURFACE_INSETS_CHANGED;
}
+ // The frame number changing is only relevant in the context of other
+ // changes, and so we don't need to track it with a flag.
+ frameNumber = o.frameNumber;
+
if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
hasManualSurfaceInsets = o.hasManualSurfaceInsets;
changes |= SURFACE_INSETS_CHANGED;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index af6c701..5b1dd5c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3333,7 +3333,7 @@
final int actionCount = mActions.size();
int nonStandardActionCount = 0;
- long defaultStandardActions = 0;
+ int defaultStandardActions = 0;
for (int i = 0; i < actionCount; i++) {
AccessibilityAction action = mActions.get(i);
if (isDefaultStandardAction(action)) {
@@ -3342,7 +3342,7 @@
nonStandardActionCount++;
}
}
- parcel.writeLong(defaultStandardActions);
+ parcel.writeInt(defaultStandardActions);
parcel.writeInt(nonStandardActionCount);
for (int i = 0; i < actionCount; i++) {
@@ -3540,7 +3540,7 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
- final long standardActions = parcel.readLong();
+ final int standardActions = parcel.readInt();
addStandardActions(standardActions);
final int nonStandardActionCount = parcel.readInt();
for (int i = 0; i < nonStandardActionCount; i++) {
@@ -3636,7 +3636,7 @@
return null;
}
- private static AccessibilityAction getActionSingletonBySerializationFlag(long flag) {
+ private static AccessibilityAction getActionSingletonBySerializationFlag(int flag) {
final int actions = AccessibilityAction.sStandardActions.size();
for (int i = 0; i < actions; i++) {
AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
@@ -3648,10 +3648,10 @@
return null;
}
- private void addStandardActions(long serializationIdMask) {
- long remainingIds = serializationIdMask;
+ private void addStandardActions(int serializationIdMask) {
+ int remainingIds = serializationIdMask;
while (remainingIds > 0) {
- final int id = 1 << Long.numberOfTrailingZeros(remainingIds);
+ final int id = 1 << Integer.numberOfTrailingZeros(remainingIds);
remainingIds &= ~id;
AccessibilityAction action = getActionSingletonBySerializationFlag(id);
addAction(action);
@@ -4276,7 +4276,7 @@
private final CharSequence mLabel;
/** @hide */
- public long mSerializationFlag = -1L;
+ public int mSerializationFlag = -1;
/**
* Creates a new AccessibilityAction. For adding a standard action without a specific label,
@@ -4310,7 +4310,7 @@
private AccessibilityAction(int standardActionId) {
this(standardActionId, null);
- mSerializationFlag = bitAt(sStandardActions.size());
+ mSerializationFlag = (int) bitAt(sStandardActions.size());
sStandardActions.add(this);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index a4261eb..1e562ea 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -358,6 +358,9 @@
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
+ /** id of last requested autofill ui */
+ @Nullable private AutofillId mIdShownFillUi;
+
/**
* Views that were already "entered" - if they're entered again when the session is not active,
* they're ignored
@@ -1547,6 +1550,7 @@
mTrackedViews = null;
mFillableIds = null;
mSaveTriggerId = null;
+ mIdShownFillUi = null;
if (resetEnteredIds) {
mEnteredIds = null;
}
@@ -1676,8 +1680,9 @@
if (client != null) {
if (client.autofillClientRequestShowFillUi(anchor, width, height,
- anchorBounds, presenter) && mCallback != null) {
+ anchorBounds, presenter)) {
callback = mCallback;
+ mIdShownFillUi = id;
}
}
}
@@ -1944,10 +1949,23 @@
}
}
- private void requestHideFillUi(AutofillId id) {
- final View anchor = findView(id);
+ /** @hide */
+ public void requestHideFillUi() {
+ requestHideFillUi(mIdShownFillUi, true);
+ }
+
+ private void requestHideFillUi(AutofillId id, boolean force) {
+ final View anchor = id == null ? null : findView(id);
if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
if (anchor == null) {
+ if (force) {
+ // When user taps outside autofill window, force to close fill ui even id does
+ // not match.
+ AutofillClient client = getClient();
+ if (client != null) {
+ client.autofillClientRequestHideFillUi();
+ }
+ }
return;
}
requestHideFillUi(id, anchor);
@@ -1963,7 +1981,8 @@
// service being uninstalled and the UI being dismissed.
AutofillClient client = getClient();
if (client != null) {
- if (client.autofillClientRequestHideFillUi() && mCallback != null) {
+ if (client.autofillClientRequestHideFillUi()) {
+ mIdShownFillUi = null;
callback = mCallback;
}
}
@@ -2655,7 +2674,7 @@
public void requestHideFillUi(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.requestHideFillUi(id));
+ afm.post(() -> afm.requestHideFillUi(id, false));
}
}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 1789edf..2b335fb 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -29,6 +29,8 @@
import android.service.textclassifier.ITextLinksCallback;
import android.service.textclassifier.ITextSelectionCallback;
+import com.android.internal.util.Preconditions;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -40,13 +42,16 @@
private static final String LOG_TAG = "SystemTextClassifier";
private final ITextClassifierService mManagerService;
+ private final TextClassificationConstants mSettings;
private final TextClassifier mFallback;
private final String mPackageName;
- SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException {
+ SystemTextClassifier(Context context, TextClassificationConstants settings)
+ throws ServiceManager.ServiceNotFoundException {
mManagerService = ITextClassifierService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
- mFallback = new TextClassifierImpl(context);
+ mSettings = Preconditions.checkNotNull(settings);
+ mFallback = new TextClassifierImpl(context, settings);
mPackageName = context.getPackageName();
}
@@ -108,6 +113,11 @@
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Utils.validate(text, false /* allowInMainThread */);
+
+ if (!mSettings.isSmartLinkifyEnabled()) {
+ return TextClassifier.NO_OP.generateLinks(text, options);
+ }
+
try {
if (options == null) {
options = new TextLinks.Options().setCallingPackageName(mPackageName);
diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
similarity index 70%
rename from core/java/android/view/textclassifier/TextClassifierConstants.java
rename to core/java/android/view/textclassifier/TextClassificationConstants.java
index 397473b..32a7f37 100644
--- a/core/java/android/view/textclassifier/TextClassifierConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -30,11 +30,15 @@
* This is encoded as a key=value list, separated by commas. Ex:
*
* <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
+ * model_dark_launch_enabled (boolean)
+ * smart_selection_enabled (boolean)
+ * smart_text_share_enabled (boolean)
+ * smart_linkify_enabled (boolean)
+ * smart_select_animation_enabled (boolean)
* suggest_selection_max_range_length (int)
* classify_text_max_range_length (int)
* generate_links_max_text_length (int)
+ * generate_links_log_sample_rate (int)
* entity_list_default (String[])
* entity_list_not_editable (String[])
* entity_list_editable (String[])
@@ -46,20 +50,24 @@
*
* Example of setting the values for testing.
* adb shell settings put global text_classifier_constants \
- * smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true,\
+ * model_dark_launch_enabled=true,smart_selection_enabled=true,\
* entity_list_default=phone:address
* @hide
*/
-public final class TextClassifierConstants {
+public final class TextClassificationConstants {
- private static final String LOG_TAG = "TextClassifierConstants";
+ private static final String LOG_TAG = "TextClassificationConstants";
- private static final String SMART_SELECTION_DARK_LAUNCH =
- "smart_selection_dark_launch";
- private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
- "smart_selection_enabled_for_edit_text";
+ private static final String MODEL_DARK_LAUNCH_ENABLED =
+ "model_dark_launch_enabled";
+ private static final String SMART_SELECTION_ENABLED =
+ "smart_selection_enabled";
+ private static final String SMART_TEXT_SHARE_ENABLED =
+ "smart_text_share_enabled";
private static final String SMART_LINKIFY_ENABLED =
"smart_linkify_enabled";
+ private static final String SMART_SELECT_ANIMATION_ENABLED =
+ "smart_select_animation_enabled";
private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
"suggest_selection_max_range_length";
private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
@@ -75,9 +83,11 @@
private static final String ENTITY_LIST_EDITABLE =
"entity_list_editable";
- private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
- private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
+ private static final boolean MODEL_DARK_LAUNCH_ENABLED_DEFAULT = false;
+ private static final boolean SMART_SELECTION_ENABLED_DEFAULT = true;
+ private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
+ private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
@@ -92,12 +102,11 @@
.add(TextClassifier.TYPE_DATE_TIME)
.add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
- /** Default settings. */
- static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
- private final boolean mDarkLaunch;
- private final boolean mSuggestSelectionEnabledForEditableText;
+ private final boolean mModelDarkLaunchEnabled;
+ private final boolean mSmartSelectionEnabled;
+ private final boolean mSmartTextShareEnabled;
private final boolean mSmartLinkifyEnabled;
+ private final boolean mSmartSelectionAnimationEnabled;
private final int mSuggestSelectionMaxRangeLength;
private final int mClassifyTextMaxRangeLength;
private final int mGenerateLinksMaxTextLength;
@@ -106,20 +115,7 @@
private final List<String> mEntityListNotEditable;
private final List<String> mEntityListEditable;
- private TextClassifierConstants() {
- mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
- mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
- mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
- mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
- mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
- mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
- mGenerateLinksLogSampleRate = GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT;
- mEntityListDefault = parseEntityList(ENTITY_LIST_DEFAULT_VALUE);
- mEntityListNotEditable = mEntityListDefault;
- mEntityListEditable = mEntityListDefault;
- }
-
- private TextClassifierConstants(@Nullable String settings) {
+ private TextClassificationConstants(@Nullable String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
try {
parser.setString(settings);
@@ -127,15 +123,21 @@
// Failed to parse the settings string, log this and move on with defaults.
Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
}
- mDarkLaunch = parser.getBoolean(
- SMART_SELECTION_DARK_LAUNCH,
- SMART_SELECTION_DARK_LAUNCH_DEFAULT);
- mSuggestSelectionEnabledForEditableText = parser.getBoolean(
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
+ mModelDarkLaunchEnabled = parser.getBoolean(
+ MODEL_DARK_LAUNCH_ENABLED,
+ MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+ mSmartSelectionEnabled = parser.getBoolean(
+ SMART_SELECTION_ENABLED,
+ SMART_SELECTION_ENABLED_DEFAULT);
+ mSmartTextShareEnabled = parser.getBoolean(
+ SMART_TEXT_SHARE_ENABLED,
+ SMART_TEXT_SHARE_ENABLED_DEFAULT);
mSmartLinkifyEnabled = parser.getBoolean(
SMART_LINKIFY_ENABLED,
SMART_LINKIFY_ENABLED_DEFAULT);
+ mSmartSelectionAnimationEnabled = parser.getBoolean(
+ SMART_SELECT_ANIMATION_ENABLED,
+ SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
mSuggestSelectionMaxRangeLength = parser.getInt(
SUGGEST_SELECTION_MAX_RANGE_LENGTH,
SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
@@ -160,22 +162,30 @@
}
/** Load from a settings string. */
- public static TextClassifierConstants loadFromString(String settings) {
- return new TextClassifierConstants(settings);
+ public static TextClassificationConstants loadFromString(String settings) {
+ return new TextClassificationConstants(settings);
}
- public boolean isDarkLaunch() {
- return mDarkLaunch;
+ public boolean isModelDarkLaunchEnabled() {
+ return mModelDarkLaunchEnabled;
}
- public boolean isSuggestSelectionEnabledForEditableText() {
- return mSuggestSelectionEnabledForEditableText;
+ public boolean isSmartSelectionEnabled() {
+ return mSmartSelectionEnabled;
+ }
+
+ public boolean isSmartTextShareEnabled() {
+ return mSmartTextShareEnabled;
}
public boolean isSmartLinkifyEnabled() {
return mSmartLinkifyEnabled;
}
+ public boolean isSmartSelectionAnimationEnabled() {
+ return mSmartSelectionAnimationEnabled;
+ }
+
public int getSuggestSelectionMaxRangeLength() {
return mSuggestSelectionMaxRangeLength;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 300aef2..fea932c 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -20,6 +20,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.os.ServiceManager;
+import android.provider.Settings;
import android.service.textclassifier.TextClassifierService;
import com.android.internal.util.Preconditions;
@@ -38,12 +39,15 @@
private final Object mLock = new Object();
private final Context mContext;
+ private final TextClassificationConstants mSettings;
private TextClassifier mTextClassifier;
private TextClassifier mSystemTextClassifier;
/** @hide */
public TextClassificationManager(Context context) {
mContext = Preconditions.checkNotNull(context);
+ mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
+ context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
}
/**
@@ -56,14 +60,14 @@
if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
try {
Log.d(LOG_TAG, "Initialized SystemTextClassifier");
- mSystemTextClassifier = new SystemTextClassifier(mContext);
+ mSystemTextClassifier = new SystemTextClassifier(mContext, mSettings);
} catch (ServiceManager.ServiceNotFoundException e) {
Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
}
}
if (mSystemTextClassifier == null) {
Log.d(LOG_TAG, "Using an in-process TextClassifier as the system default");
- mSystemTextClassifier = new TextClassifierImpl(mContext);
+ mSystemTextClassifier = new TextClassifierImpl(mContext, mSettings);
}
}
return mSystemTextClassifier;
@@ -78,7 +82,7 @@
if (isSystemTextClassifierEnabled()) {
mTextClassifier = getSystemDefaultTextClassifier();
} else {
- mTextClassifier = new TextClassifierImpl(mContext);
+ mTextClassifier = new TextClassifierImpl(mContext, mSettings);
}
}
return mTextClassifier;
@@ -100,4 +104,17 @@
return SYSTEM_TEXT_CLASSIFIER_ENABLED
&& TextClassifierService.getServiceComponentName(mContext) != null;
}
+
+ /** @hide */
+ public static TextClassificationConstants getSettings(Context context) {
+ Preconditions.checkNotNull(context);
+ final TextClassificationManager tcm =
+ context.getSystemService(TextClassificationManager.class);
+ if (tcm != null) {
+ return tcm.mSettings;
+ } else {
+ return TextClassificationConstants.loadFromString(Settings.Global.getString(
+ context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+ }
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index d52a30b..0321bb6 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -329,14 +329,6 @@
}
/**
- * Returns this TextClassifier's settings.
- * @hide
- */
- default TextClassifierConstants getSettings() {
- return TextClassifierConstants.DEFAULT;
- }
-
- /**
* Configuration object for specifying what entities to identify.
*
* Configs are initially based on a predefined preset, and can be modified from there.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 5b7095b..41f1c69 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -34,7 +34,6 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.provider.Settings;
import android.view.textclassifier.logging.DefaultLogger;
import android.view.textclassifier.logging.GenerateLinksLogger;
import android.view.textclassifier.logging.Logger;
@@ -99,13 +98,13 @@
@GuardedBy("mLoggerLock") // Do not access outside this lock.
private Logger mLogger; // Should never be null if mLoggerConfig.get() is not null.
- private TextClassifierConstants mSettings;
+ private final TextClassificationConstants mSettings;
- public TextClassifierImpl(Context context) {
+ public TextClassifierImpl(Context context, TextClassificationConstants settings) {
mContext = Preconditions.checkNotNull(context);
mFallback = TextClassifier.NO_OP;
- mGenerateLinksLogger = new GenerateLinksLogger(
- getSettings().getGenerateLinksLogSampleRate());
+ mSettings = Preconditions.checkNotNull(settings);
+ mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
}
/** @inheritDoc */
@@ -117,7 +116,7 @@
try {
final int rangeLength = selectionEndIndex - selectionStartIndex;
if (text.length() > 0
- && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
+ && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
final Calendar refTime = Calendar.getInstance();
@@ -126,7 +125,7 @@
final String string = text.toString();
final int start;
final int end;
- if (getSettings().isDarkLaunch() && !darkLaunchAllowed) {
+ if (mSettings.isModelDarkLaunchEnabled() && !darkLaunchAllowed) {
start = selectionStartIndex;
end = selectionEndIndex;
} else {
@@ -179,7 +178,7 @@
Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
try {
final int rangeLength = endIndex - startIndex;
- if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
+ if (text.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
final String string = text.toString();
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
@@ -214,7 +213,7 @@
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
- if (!getSettings().isSmartLinkifyEnabled()) {
+ if (!mSettings.isSmartLinkifyEnabled()) {
return builder.build();
}
@@ -226,7 +225,7 @@
options != null && options.getEntityConfig() != null
? options.getEntityConfig().resolveEntityListModifications(
getEntitiesForHints(options.getEntityConfig().getHints()))
- : getSettings().getEntityListDefault();
+ : mSettings.getEntityListDefault();
final TextClassifierImplNative nativeImpl =
getNative(defaultLocales);
final TextClassifierImplNative.AnnotatedSpan[] annotations =
@@ -268,7 +267,7 @@
/** @inheritDoc */
@Override
public int getMaxGenerateLinksTextLength() {
- return getSettings().getGenerateLinksMaxTextLength();
+ return mSettings.getGenerateLinksMaxTextLength();
}
private Collection<String> getEntitiesForHints(Collection<String> hints) {
@@ -278,11 +277,11 @@
// Use the default if there is no hint, or conflicting ones.
final boolean useDefault = editable == notEditable;
if (useDefault) {
- return getSettings().getEntityListDefault();
+ return mSettings.getEntityListDefault();
} else if (editable) {
- return getSettings().getEntityListEditable();
+ return mSettings.getEntityListEditable();
} else { // notEditable
- return getSettings().getEntityListNotEditable();
+ return mSettings.getEntityListNotEditable();
}
}
@@ -298,16 +297,6 @@
}
}
- /** @hide */
- @Override
- public TextClassifierConstants getSettings() {
- if (mSettings == null) {
- mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
- mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
- }
- return mSettings;
- }
-
private TextClassifierImplNative getNative(LocaleList localeList)
throws FileNotFoundException {
synchronized (mLock) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 84c000a..ed122a6 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -39,7 +39,7 @@
// "file:///android_res/drawable/bar.png". Use "drawable" to refer to
// "drawable-hdpi" directory as well.
static final String RESOURCE_BASE = "file:///android_res/";
- static final String FILE_BASE = "file://";
+ static final String FILE_BASE = "file:";
static final String PROXY_BASE = "file:///cookieless_proxy/";
static final String CONTENT_BASE = "content:";
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 4167ad4..07593a5 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -93,13 +93,11 @@
synchronized (sLock) {
sMultiprocessEnabled = enabled;
- // When toggling between multi-process being on/off, start or stop the
- // zygote. If it is enabled and the zygote is not yet started, launch it.
- // Otherwise, kill it. The name may be null if the package information has
- // not yet been resolved.
- if (enabled) {
- connectToZygoteIfNeededLocked();
- } else {
+ // When multi-process is disabled, kill the zygote. When it is enabled,
+ // the zygote is not explicitly started here to avoid waiting on the
+ // zygote launch at boot. Instead, the zygote will be started when it is
+ // first needed in getProcess().
+ if (!enabled) {
stopZygoteLocked();
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2e7b2fd..02f35ca 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -107,6 +107,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextLinks;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView.Drawables;
@@ -4024,7 +4025,7 @@
private void updateAssistMenuItems(Menu menu) {
clearAssistMenuItems(menu);
- if (!mTextView.isDeviceProvisioned()) {
+ if (!shouldEnableAssistMenuItems()) {
return;
}
final TextClassification textClassification =
@@ -4097,7 +4098,7 @@
final TextClassification textClassification =
getSelectionActionModeHelper().getTextClassification();
- if (!mTextView.isDeviceProvisioned() || textClassification == null) {
+ if (!shouldEnableAssistMenuItems() || textClassification == null) {
// No textClassification result to handle the click. Eat the click.
return true;
}
@@ -4118,6 +4119,12 @@
return true;
}
+ private boolean shouldEnableAssistMenuItems() {
+ return mTextView.isDeviceProvisioned()
+ && TextClassificationManager.getSettings(mTextView.getContext())
+ .isSmartTextShareEnabled();
+ }
+
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
getSelectionActionModeHelper().onSelectionAction(item.getItemId());
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 273b9ed..4fb303e 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -20,15 +20,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.media.SessionToken2;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
import android.media.update.MediaControlView2Provider;
import android.media.update.ViewGroupHelper;
import android.util.AttributeSet;
+import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+// TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden.
/**
* A View that contains the controls for MediaPlayer2.
* It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
@@ -55,10 +58,7 @@
* <p>
* It is also possible to add custom buttons with custom icons and actions inside MediaControlView2.
* Those buttons will be shown when the overflow button is clicked.
- * See {@link VideoView2#setCustomActions} for more details on how to add.
- *
- * TODO PUBLIC API
- * @hide
+ * See VideoView2#setCustomActions for more details on how to add.
*/
public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
/** @hide */
@@ -137,16 +137,18 @@
/**
* String for receiving command to show subtitle from MediaSession. Can be checked by
* implementing {@link android.media.session.MediaSession.Callback#onCommand}
+ * @hide
*/
public static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
/**
* String for receiving command to hide subtitle from MediaSession. Can be checked by
* implementing {@link android.media.session.MediaSession.Callback#onCommand}
+ * @hide
*/
public static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
+
/**
- * String for receiving command to set fullscreen from MediaSession. Can be checked by
- * implementing {@link android.media.session.MediaSession.Callback#onCommand}
+ * @hide TODO: remove once the implementation is revised
*/
public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
@@ -174,10 +176,24 @@
}
/**
- * Sets MediaController instance to control corresponding MediaSession.
+ * Sets MediaSession2 token to control corresponding MediaSession2.
+ */
+ public void setMediaSessionToken(SessionToken2 token) {
+ mProvider.setMediaSessionToken_impl(token);
+ }
+
+ /**
+ * Registers a callback to be invoked when the fullscreen mode should be changed.
+ * @param l The callback that will be run
+ */
+ public void setOnFullScreenListener(OnFullScreenListener l) {
+ mProvider.setOnFullScreenListener_impl(l);
+ }
+
+ /**
+ * @hide TODO: remove once the implementation is revised
*/
public void setController(MediaController controller) {
- mProvider.setController_impl(controller);
}
/**
@@ -215,4 +231,15 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mProvider.onLayout_impl(changed, l, t, r, b);
}
+
+ /**
+ * Interface definition of a callback to be invoked to inform the fullscreen mode is changed.
+ * Application should handle the fullscreen mode accordingly.
+ */
+ public interface OnFullScreenListener {
+ /**
+ * Called to indicate a fullscreen mode change.
+ */
+ void onFullScreen(View view, boolean fullScreen);
+ }
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6ab09d6..12ab0ee 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -65,12 +67,10 @@
private static final String LOG_TAG = "SelectActionModeHelper";
- // TODO: Make this a configurable flag.
- private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
-
private final Editor mEditor;
private final TextView mTextView;
private final TextClassificationHelper mTextClassificationHelper;
+ private final TextClassificationConstants mTextClassificationSettings;
private TextClassification mTextClassification;
private AsyncTask mTextClassificationAsyncTask;
@@ -84,6 +84,7 @@
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
mTextView = mEditor.getTextView();
+ mTextClassificationSettings = TextClassificationManager.getSettings(mTextView.getContext());
mTextClassificationHelper = new TextClassificationHelper(
mTextView.getContext(),
mTextView.getTextClassifier(),
@@ -91,7 +92,7 @@
0, 1, mTextView.getTextLocales());
mSelectionTracker = new SelectionTracker(mTextView);
- if (SMART_SELECT_ANIMATION_ENABLED) {
+ if (mTextClassificationSettings.isSmartSelectionAnimationEnabled()) {
mSmartSelectSprite = new SmartSelectSprite(mTextView.getContext(),
editor.getTextView().mHighlightColor, mTextView::invalidate);
} else {
@@ -104,9 +105,7 @@
*/
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
- adjustSelection &= !mTextView.isTextEditable()
- || mTextView.getTextClassifier().getSettings()
- .isSuggestSelectionEnabledForEditableText();
+ adjustSelection &= mTextClassificationSettings.isSmartSelectionEnabled();
mSelectionTracker.onOriginalSelection(
getText(mTextView),
@@ -249,7 +248,7 @@
|| mTextView.isTextEditable()
|| actionMode == Editor.TextActionMode.TEXT_LINK)) {
// Do not change the selection if TextClassifier should be dark launched.
- if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
+ if (!mTextClassificationSettings.isModelDarkLaunchEnabled()) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextView.invalidate();
}
@@ -450,7 +449,6 @@
selectionEnd = mTextView.getSelectionEnd();
}
mTextClassificationHelper.init(
- mTextView.getContext(),
mTextView.getTextClassifier(),
getText(mTextView),
selectionStart, selectionEnd,
@@ -882,7 +880,8 @@
private static final int TRIM_DELTA = 120; // characters
- private Context mContext;
+ private final Context mContext;
+ private final boolean mDarkLaunchEnabled;
private TextClassifier mTextClassifier;
/** The original TextView text. **/
@@ -917,13 +916,15 @@
TextClassificationHelper(Context context, TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- init(context, textClassifier, text, selectionStart, selectionEnd, locales);
+ init(textClassifier, text, selectionStart, selectionEnd, locales);
+ mContext = Preconditions.checkNotNull(context);
+ mDarkLaunchEnabled = TextClassificationManager.getSettings(mContext)
+ .isModelDarkLaunchEnabled();
}
@UiThread
- public void init(Context context, TextClassifier textClassifier,
- CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- mContext = Preconditions.checkNotNull(context);
+ public void init(TextClassifier textClassifier, CharSequence text,
+ int selectionStart, int selectionEnd, LocaleList locales) {
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
mLastClassificationText = null; // invalidate.
@@ -956,7 +957,7 @@
mSelectionOptions.getDefaultLocales());
}
// Do not classify new selection boundaries if TextClassifier should be dark launched.
- if (!mTextClassifier.getSettings().isDarkLaunch()) {
+ if (!mDarkLaunchEnabled) {
mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
mSelectionEnd = Math.min(
mText.length(), selection.getSelectionEndIndex() + mTrimStart);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2cfdb76..50e6393 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -319,6 +319,11 @@
// Enum for the "typeface" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
+ /** @hide */
+ @IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface XMLTypefaceAttr{}
+ private static final int DEFAULT_TYPEFACE = -1;
private static final int SANS = 1;
private static final int SERIF = 2;
private static final int MONOSPACE = 3;
@@ -1976,33 +1981,52 @@
}
}
- private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex,
- int styleIndex) {
- Typeface tf = fontTypeface;
- if (tf == null && familyName != null) {
- tf = Typeface.create(familyName, styleIndex);
- } else if (tf != null && tf.getStyle() != styleIndex) {
- tf = Typeface.create(tf, styleIndex);
+ /**
+ * Sets the Typeface taking into account the given attributes.
+ *
+ * @param typeface a typeface
+ * @param familyName family name string, e.g. "serif"
+ * @param typefaceIndex an index of the typeface enum, e.g. SANS, SERIF.
+ * @param style a typeface style
+ * @param weight a weight value for the Typeface or -1 if not specified.
+ */
+ private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
+ @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
+ @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+ if (typeface == null && familyName != null) {
+ // Lookup normal Typeface from system font map.
+ final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
+ resolveStyleAndSetTypeface(normalTypeface, style, weight);
+ } else if (typeface != null) {
+ resolveStyleAndSetTypeface(typeface, style, weight);
+ } else { // both typeface and familyName is null.
+ switch (typefaceIndex) {
+ case SANS:
+ resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);
+ break;
+ case SERIF:
+ resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);
+ break;
+ case MONOSPACE:
+ resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);
+ break;
+ case DEFAULT_TYPEFACE:
+ default:
+ resolveStyleAndSetTypeface(null, style, weight);
+ break;
+ }
}
- if (tf != null) {
- setTypeface(tf);
- return;
+ }
+
+ private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
+ @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+ if (weight >= 0) {
+ weight = Math.min(Typeface.MAX_WEIGHT, weight);
+ final boolean italic = (style & Typeface.ITALIC) != 0;
+ setTypeface(Typeface.create(typeface, weight, italic));
+ } else {
+ setTypeface(Typeface.create(typeface, style));
}
- switch (typefaceIndex) {
- case SANS:
- tf = Typeface.SANS_SERIF;
- break;
-
- case SERIF:
- tf = Typeface.SERIF;
- break;
-
- case MONOSPACE:
- tf = Typeface.MONOSPACE;
- break;
- }
-
- setTypeface(tf, styleIndex);
}
private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
@@ -3392,6 +3416,7 @@
boolean mFontFamilyExplicit = false;
int mTypefaceIndex = -1;
int mStyleIndex = -1;
+ int mFontWeight = -1;
boolean mAllCaps = false;
int mShadowColor = 0;
float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
@@ -3416,6 +3441,7 @@
+ " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
+ " mTypefaceIndex:" + mTypefaceIndex + "\n"
+ " mStyleIndex:" + mStyleIndex + "\n"
+ + " mFontWeight:" + mFontWeight + "\n"
+ " mAllCaps:" + mAllCaps + "\n"
+ " mShadowColor:" + mShadowColor + "\n"
+ " mShadowDx:" + mShadowDx + "\n"
@@ -3451,6 +3477,8 @@
com.android.internal.R.styleable.TextAppearance_fontFamily);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle,
com.android.internal.R.styleable.TextAppearance_textStyle);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_textFontWeight,
+ com.android.internal.R.styleable.TextAppearance_textFontWeight);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps,
com.android.internal.R.styleable.TextAppearance_textAllCaps);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor,
@@ -3536,6 +3564,9 @@
case com.android.internal.R.styleable.TextAppearance_textStyle:
attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex);
break;
+ case com.android.internal.R.styleable.TextAppearance_textFontWeight:
+ attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight);
+ break;
case com.android.internal.R.styleable.TextAppearance_textAllCaps:
attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps);
break;
@@ -3598,7 +3629,7 @@
attributes.mFontFamily = null;
}
setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily,
- attributes.mTypefaceIndex, attributes.mStyleIndex);
+ attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight);
if (attributes.mShadowColor != 0) {
setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy,
@@ -5938,15 +5969,19 @@
boolean forceUpdate = false;
if (isPassword) {
setTransformationMethod(PasswordTransformationMethod.getInstance());
- setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+ setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+ Typeface.NORMAL, -1 /* weight, not specifeid */);
} else if (isVisiblePassword) {
if (mTransformation == PasswordTransformationMethod.getInstance()) {
forceUpdate = true;
}
- setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+ setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+ Typeface.NORMAL, -1 /* weight, not specified */);
} else if (wasPassword || wasVisiblePassword) {
// not in password mode, clean up typeface and transformation
- setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1);
+ setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */,
+ DEFAULT_TYPEFACE /* typeface index */, Typeface.NORMAL,
+ -1 /* weight, not specified */);
if (mTransformation == PasswordTransformationMethod.getInstance()) {
forceUpdate = true;
}
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index a7ae3234..09ff337 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -22,8 +22,12 @@
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
import android.media.MediaMetadata2;
+import android.media.MediaPlayer2;
import android.media.MediaPlayerBase;
+import android.media.SessionToken2;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
@@ -45,14 +49,14 @@
// TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted.
/**
- * Displays a video file. VideoView2 class is a View class which is wrapping MediaPlayer2 so that
- * developers can easily implement a video rendering application.
+ * 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.
*
* <p>
* <em> Data sources that VideoView2 supports : </em>
* VideoView2 can play video files and audio-only files as
* well. It can load from various sources such as resources or content providers. The supported
- * media file formats are the same as MediaPlayer2.
+ * media file formats are the same as {@link MediaPlayer2}.
*
* <p>
* <em> View type can be selected : </em>
@@ -101,8 +105,6 @@
* does not restore the current play state, play position, selected tracks. Applications should save
* and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
* {@link android.app.Activity#onRestoreInstanceState}.
- *
- * @hide
*/
public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
/** @hide */
@@ -199,12 +201,24 @@
* before calling this method.
*
* @throws IllegalStateException if interal MediaSession is not created yet.
+ * @hide TODO: remove
*/
public MediaController getMediaController() {
return mProvider.getMediaController_impl();
}
/**
+ * Returns {@link android.media.SessionToken2} so that developers create their own
+ * {@link android.media.MediaController2} instance. This method should be called when VideoView2
+ * is attached to window, or it throws IllegalStateException.
+ *
+ * @throws IllegalStateException if interal MediaSession is not created yet.
+ */
+ public SessionToken2 getMediaSessionToken() {
+ return mProvider.getMediaSessionToken_impl();
+ }
+
+ /**
* Shows or hides closed caption or subtitles if there is any.
* The first subtitle track will be chosen if there multiple subtitle tracks exist.
* Default behavior of VideoView2 is not showing subtitle.
@@ -265,6 +279,7 @@
mProvider.setAudioAttributes_impl(attributes);
}
+ // TODO: unhide this method when MediaPlayerInterface became unhidden.
/**
* Sets a remote player for handling playback of the selected route from MediaControlView2.
* If this is not called, MediaCotrolView2 will not show the route button.
@@ -302,6 +317,8 @@
* Sets video path.
*
* @param path the path of the video.
+ *
+ * @hide TODO remove
*/
public void setVideoPath(String path) {
mProvider.setVideoPath_impl(path);
@@ -311,6 +328,8 @@
* Sets video URI.
*
* @param uri the URI of the video.
+ *
+ * @hide TODO remove
*/
public void setVideoUri(Uri uri) {
mProvider.setVideoUri_impl(uri);
@@ -325,12 +344,33 @@
* changed with key/value pairs through the headers parameter with
* "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
* to disallow or allow cross domain redirection.
+ *
+ * @hide TODO remove
*/
public void setVideoUri(Uri uri, Map<String, String> headers) {
mProvider.setVideoUri_impl(uri, headers);
}
/**
+ * Sets {@link MediaItem2} object to render using VideoView2. Alternative way to set media
+ * object to VideoView2 is {@link #setDataSource}.
+ * @param mediaItem the MediaItem2 to play
+ * @see #setDataSource
+ */
+ public void setMediaItem(@NonNull MediaItem2 mediaItem) {
+ mProvider.setMediaItem_impl(mediaItem);
+ }
+
+ /**
+ * Sets {@link DataSourceDesc} object to render using VideoView2.
+ * @param dataSource the {@link DataSourceDesc} object to play.
+ * @see #setMediaItem
+ */
+ public void setDataSource(@NonNull DataSourceDesc dataSource) {
+ mProvider.setDataSource_impl(dataSource);
+ }
+
+ /**
* Selects which view will be used to render video between SurfacView and TextureView.
*
* @param viewType the view type to render video
@@ -361,6 +401,7 @@
* in {@link MediaControlView2}.
* @param executor executor to run callbacks on.
* @param listener A listener to be called when a custom button is clicked.
+ * @hide TODO remove
*/
public void setCustomActions(List<PlaybackState.CustomAction> actionList,
Executor executor, OnCustomActionListener listener) {
@@ -371,7 +412,6 @@
* Registers a callback to be invoked when a view type change is done.
* {@see #setViewType(int)}
* @param l The callback that will be run
- *
* @hide
*/
@VisibleForTesting
@@ -382,6 +422,7 @@
/**
* Registers a callback to be invoked when the fullscreen mode should be changed.
* @param l The callback that will be run
+ * @hide TODO remove
*/
public void setFullScreenRequestListener(OnFullScreenRequestListener l) {
mProvider.setFullScreenRequestListener_impl(l);
@@ -410,6 +451,7 @@
/**
* Interface definition of a callback to be invoked to inform the fullscreen mode is changed.
* Application should handle the fullscreen mode accordingly.
+ * @hide TODO remove
*/
public interface OnFullScreenRequestListener {
/**
@@ -420,8 +462,8 @@
/**
* Interface definition of a callback to be invoked to inform that a custom action is performed.
+ * @hide TODO remove
*/
- // TODO: When MediaSession2 is ready, modify the method to match the signature.
public interface OnCustomActionListener {
/**
* Called to indicate that a custom action is performed.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index fabda4a..2505ea5 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -26,7 +26,8 @@
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
- int startOperation(IBinder token, int code, int uid, String packageName);
+ int startOperation(IBinder token, int code, int uid, String packageName,
+ boolean startIfModeDefault);
void finishOperation(IBinder token, int code, int uid, String packageName);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8ee31f7..242f422 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -11597,7 +11597,7 @@
* time at the highest power level.
* @param activityInfo
*/
- private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
if (activityInfo == null) {
return;
}
@@ -13348,7 +13348,7 @@
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
- private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
+ private static final boolean DEFAULT_READ_BINARY_CPU_TIME = true;
private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 5eecd9c..09adc82 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -23,16 +23,28 @@
YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
}
+struct ErrorMgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf jmp;
+};
+
+void error_exit(j_common_ptr cinfo) {
+ ErrorMgr* err = (ErrorMgr*) cinfo->err;
+ (*cinfo->err->output_message) (cinfo);
+ longjmp(err->jmp, 1);
+}
+
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
int height, int* offsets, int jpegQuality) {
jpeg_compress_struct cinfo;
- jpeg_error_mgr err;
+ ErrorMgr err;
skjpeg_destination_mgr sk_wstream(stream);
- cinfo.err = jpeg_std_error(&err);
- err.error_exit = skjpeg_error_exit;
- jmp_buf jmp;
- if (setjmp(jmp)) {
+ cinfo.err = jpeg_std_error(&err.pub);
+ err.pub.error_exit = error_exit;
+
+ if (setjmp(err.jmp)) {
+ jpeg_destroy_compress(&cinfo);
return false;
}
jpeg_create_compress(&cinfo);
@@ -47,6 +59,8 @@
jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
return true;
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d18c172..5e2cd40 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -28,9 +28,9 @@
#include <sys/types.h>
#include <unistd.h>
-#include <utils/Atomic.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
+#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <utils/List.h>
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index d17993a..1b206fd 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -35,8 +35,8 @@
#include <binder/Parcel.h>
#include <binder/BpBinder.h>
#include <binder/ProcessState.h>
+#include <cutils/atomic.h>
#include <log/log.h>
-#include <utils/Atomic.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index 834ecde..74b47d2 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -35,9 +35,9 @@
optional uint32 mnc = 3;
repeated LocaleProto locales = 4;
optional uint32 screen_layout = 5;
- optional uint32 hdr_color_mode = 6;
- optional uint32 wide_color_gamut = 7;
- optional uint32 touchscreen = 8;
+ optional uint32 color_mode = 6;
+ optional uint32 touchscreen = 7;
+ optional uint32 keyboard = 8;
optional uint32 keyboard_hidden = 9;
optional uint32 hard_keyboard_hidden = 10;
optional uint32 navigation = 11;
diff --git a/core/proto/android/os/data.proto b/core/proto/android/os/data.proto
new file mode 100644
index 0000000..c06f318
--- /dev/null
+++ b/core/proto/android/os/data.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+// This file contains protobuf definitions used in incidentd directly.
+// The top level proto message must be used as a new SectionType in
+// incidentd.
+
+// Output of SECTION_GZIP section type, which reads a file, gzip it and attached
+// in incident report as a proto field, example is LAST_KMSG.
+// NOTE the content in the file must not contain sensitive PII otherwise
+// implement it with fine-grained proto definition.
+message GZippedFileProto {
+ optional string filename = 1;
+
+ optional bytes gzipped_data = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9a53b89..7326829 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@
import "frameworks/base/core/proto/android/os/batterytype.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
+import "frameworks/base/core/proto/android/os/data.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.proto";
@@ -52,9 +53,8 @@
package android.os;
-// privacy field options must not be set at this level because all
-// the sections are able to be controlled and configured by section ids.
-// Instead privacy field options need to be configured in each section proto message.
+// Privacy tag can be marked to override UNSET messages so generic
+// message type can be handled case by case, e.g. GZippedFileProto.
message IncidentProto {
reserved 1001;
@@ -151,6 +151,12 @@
(section).args = "/sys/class/power_supply/bms/battery_type"
];
+ optional GZippedFileProto last_kmsg = 2007 [
+ (section).type = SECTION_GZIP,
+ (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg",
+ (privacy).dest = DEST_AUTOMATIC
+ ];
+
// System Services
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
(section).type = SECTION_DUMPSYS,
@@ -208,37 +214,37 @@
(section).args = "procstats --proto"
];
- optional com.android.server.am.proto.ActivityStackSupervisorProto activities = 3012 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto activities = 3012 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto activities"
];
- optional com.android.server.am.proto.BroadcastProto broadcasts = 3013 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto broadcasts = 3013 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto broadcasts"
];
- optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpServicesProto amservices = 3014 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto service"
];
- optional com.android.server.am.proto.ProcessesProto amprocesses = 3015 [
+ optional com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto amprocesses = 3015 [
(section).type = SECTION_DUMPSYS,
(section).args = "activity --proto processes"
];
- optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
+ optional com.android.server.AlarmManagerServiceDumpProto alarm = 3016 [
(section).type = SECTION_DUMPSYS,
(section).args = "alarm --proto"
];
- optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [
+ optional com.android.server.wm.proto.WindowManagerServiceDumpProto window = 3017 [
(section).type = SECTION_DUMPSYS,
(section).args = "window --proto"
];
- optional com.android.server.am.proto.MemInfoProto meminfo = 3018 [
+ optional com.android.server.am.proto.MemInfoDumpProto meminfo = 3018 [
(section).type = SECTION_DUMPSYS,
(section).args = "meminfo -a --proto"
];
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 788d901..3b9150f 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -40,16 +40,22 @@
message ActivityManagerServiceProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional ActivityStackSupervisorProto activities = 1;
+ optional ActivityManagerServiceDumpActivitiesProto activities = 1;
- optional BroadcastProto broadcasts = 2;
+ optional ActivityManagerServiceDumpBroadcastsProto broadcasts = 2;
- optional ActiveServicesProto services = 3;
+ optional ActivityManagerServiceDumpServicesProto services = 3;
- optional ProcessesProto processes = 4;
+ optional ActivityManagerServiceDumpProcessesProto processes = 4;
}
// "dumpsys activity --proto activities"
+message ActivityManagerServiceDumpActivitiesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ActivityStackSupervisorProto activity_stack_supervisor = 1;
+}
+
message ActivityStackSupervisorProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -58,6 +64,9 @@
optional KeyguardControllerProto keyguard_controller = 3;
optional int32 focused_stack_id = 4;
optional .com.android.server.wm.proto.IdentifierProto resumed_activity = 5;
+ // Whether or not the home activity is the recents activity. This is needed for the CTS tests to
+ // know what activity types to check for when invoking splitscreen multi-window.
+ optional bool is_home_recents_component = 6;
}
/* represents ActivityStackSupervisor.ActivityDisplay */
@@ -118,7 +127,7 @@
}
// "dumpsys activity --proto broadcasts"
-message BroadcastProto {
+message ActivityManagerServiceDumpBroadcastsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated ReceiverListProto receiver_list = 1;
@@ -199,7 +208,7 @@
repeated BroadcastSummary historical_broadcasts_summary = 6;
}
-message MemInfoProto {
+message MemInfoDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 uptime_duration_ms = 1;
@@ -403,6 +412,12 @@
}
// "dumpsys activity --proto service"
+message ActivityManagerServiceDumpServicesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ActiveServicesProto active_services = 1;
+}
+
message ActiveServicesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -582,7 +597,7 @@
}
// TODO: "dumpsys activity --proto processes"
-message ProcessesProto {
+message ActivityManagerServiceDumpProcessesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated ProcessRecordProto procs = 1;
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index b288c11..53e3ba9 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -28,7 +28,7 @@
option java_multiple_files = true;
// next ID: 43
-message AlarmManagerServiceProto {
+message AlarmManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 current_time = 1;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c11058a..9598f24 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -32,7 +32,7 @@
option java_multiple_files = true;
-message WindowManagerServiceProto {
+message WindowManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional WindowManagerPolicyProto policy = 1;
@@ -295,7 +295,6 @@
optional bool animating_exit = 14;
repeated WindowStateProto child_windows = 15;
optional .android.graphics.RectProto surface_position = 16;
- optional .android.graphics.RectProto shown_position = 17;
optional int32 requested_width = 18;
optional int32 requested_height = 19;
optional int32 view_visibility = 20;
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
index d96953e..96a90bf 100644
--- a/core/proto/android/server/windowmanagertrace.proto
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -48,5 +48,5 @@
/* where the trace originated */
optional string where = 2;
- optional WindowManagerServiceProto window_manager_service = 3;
+ optional WindowManagerServiceDumpProto window_manager_service = 3;
}
diff --git a/core/res/res/drawable/ic_alert_window_layer.xml b/core/res/res/drawable/ic_alert_window_layer.xml
new file mode 100644
index 0000000..15931b8
--- /dev/null
+++ b/core/res/res/drawable/ic_alert_window_layer.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_camera.xml b/core/res/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..2921a68
--- /dev/null
+++ b/core/res/res/drawable/ic_camera.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_mic.xml b/core/res/res/drawable/ic_mic.xml
new file mode 100644
index 0000000..3212330
--- /dev/null
+++ b/core/res/res/drawable/ic_mic.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml
index c1795640..2f94d0c 100644
--- a/core/res/res/drawable/progress_horizontal_material.xml
+++ b/core/res/res/drawable/progress_horizontal_material.xml
@@ -19,6 +19,7 @@
android:gravity="center_vertical|fill_horizontal">
<shape android:shape="rectangle"
android:tint="?attr/colorControlNormal">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -28,6 +29,7 @@
<scale android:scaleWidth="100%">
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -38,6 +40,7 @@
<scale android:scaleWidth="100%">
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/progress_bar_height_material" />
<solid android:color="@color/white" />
</shape>
diff --git a/core/res/res/drawable/seekbar_track_material.xml b/core/res/res/drawable/seekbar_track_material.xml
index e88a73f..62ef136 100644
--- a/core/res/res/drawable/seekbar_track_material.xml
+++ b/core/res/res/drawable/seekbar_track_material.xml
@@ -19,6 +19,7 @@
android:gravity="center_vertical|fill_horizontal">
<shape android:shape="rectangle"
android:tint="@color/control_nodisable_material">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_background_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -32,6 +33,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_progress_height_material" />
<solid android:color="@color/white_disabled_material" />
</shape>
@@ -48,6 +50,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorControlActivated">
+ <corners android:radius="?attr/progressBarCornerRadius" />
<size android:height="@dimen/seekbar_track_progress_height_material" />
<solid android:color="@color/white" />
</shape>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 20bdf3f..c03cf51 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
+<!-- extends ViewGroup -->
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@style/Theme.Material.Notification"
@@ -126,5 +126,42 @@
android:visibility="gone"
android:contentDescription="@string/notification_work_profile_content_description"
/>
+
+ <LinearLayout
+ android:id="@+id/app_ops"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal" >
+ <ImageButton
+ android:id="@+id/camera"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_camera"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="6dp"
+ android:visibility="gone"
+ />
+ <ImageButton
+ android:id="@+id/mic"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_mic"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="4dp"
+ android:visibility="gone"
+ />
+ <ImageButton
+ android:id="@+id/overlay"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_alert_window_layer"
+ android:tint="@color/notification_secondary_text_color_light"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_marginStart="4dp"
+ android:visibility="gone"
+ />
+ </LinearLayout>
</NotificationHeaderView>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 4f0c0fb..265eaaf 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -27,6 +27,9 @@
<dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
<dimen name="preference_widget_width">72dp</dimen>
+ <!-- Height of the status bar -->
+ <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
+
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 96a83f8..7bf3e0e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1019,6 +1019,9 @@
<!-- Corner radius of buttons. -->
<attr name="buttonCornerRadius" format="dimension" />
+ <!-- Corner radius of progress bars. -->
+ <attr name="progressBarCornerRadius" format="dimension" />
+
<!-- Style for the search query widget. -->
<attr name="searchViewStyle" format="reference" />
@@ -4472,6 +4475,8 @@
<attr name="textSize" />
<!-- Style (normal, bold, italic, bold|italic) for the text. -->
<attr name="textStyle" />
+ <!-- Weight for the font used in the TextView. -->
+ <attr name="textFontWeight" />
<!-- Typeface (normal, sans, serif, monospace) for the text. -->
<attr name="typeface" />
<!-- Font family (named by string or as a font resource reference) for the text. -->
@@ -4561,6 +4566,8 @@
<attr name="typeface" />
<!-- Style (normal, bold, italic, bold|italic) for the text. -->
<attr name="textStyle" />
+ <!-- Weight for the font used in the TextView. -->
+ <attr name="textFontWeight" />
<!-- Font family (named by string or as a font resource reference) for the text. -->
<attr name="fontFamily" />
<!-- Text color for links. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cfb5784..c4fa190 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2348,6 +2348,16 @@
<attr name="logo" />
<attr name="priority" />
<attr name="autoVerify" />
+ <!-- Within an application, multiple intent filters may match a particular
+ intent. This allows the app author to specify the order filters should
+ be considered. We don't want to use priority because that is global
+ across applications.
+ <p>Only use if you really need to forcibly set the order in which
+ filters are evaluated. It is preferred to target an activity with a
+ directed intent instead.
+ <p>The value is a single integer, with higher numbers considered to
+ be better. If not specified, the default order is 0. -->
+ <attr name="order" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d6f3463..f3aa054 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3331,6 +3331,8 @@
<dimen name="config_dialogCornerRadius">2dp</dimen>
<!-- Corner radius of system buttons -->
<dimen name="config_buttonCornerRadius">@dimen/control_corner_material</dimen>
+ <!-- Corner radius of system progress bars -->
+ <dimen name="config_progressBarCornerRadius">@dimen/progress_bar_corner_material</dimen>
<!-- Controls whether system buttons use all caps for text -->
<bool name="config_buttonTextAllCaps">true</bool>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2918260..7ff96fa 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -32,7 +32,11 @@
<dimen name="toast_y_offset">24dp</dimen>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">24dp</dimen>
+ <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
+ <!-- Height of the status bar in portrait -->
+ <dimen name="status_bar_height_portrait">24dp</dimen>
+ <!-- Height of the status bar in landscape -->
+ <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index e3fdcec..210f30e 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -135,6 +135,7 @@
<dimen name="seekbar_track_progress_height_material">2dp</dimen>
<dimen name="progress_bar_height_material">4dp</dimen>
+ <dimen name="progress_bar_corner_material">0dp</dimen>
<!-- Material time picker dimensions. -->
<!-- Text size for the time picker header HH:MM label. This value is large
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a5ba4c6..c4006b3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2872,6 +2872,7 @@
<public name="urlBarResourceId" />
<!-- @hide @SystemApi -->
<public name="userRestriction" />
+ <public name="textFontWeight" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5c9f863..c3ae5fa 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -379,7 +379,7 @@
<!-- Text message in the factory reset warning dialog. This says that the the device admin app
is missing or corrupted. As a result the device will be erased. [CHAR LIMIT=NONE]-->
<string name="factory_reset_message">The admin app can\'t be used. Your device will now be
- erased.\n\nIf you have questions, contact your organization's admin.</string>
+ erased.\n\nIf you have questions, contact your organization\'s admin.</string>
<!-- A toast message displayed when printing is attempted but disabled by policy. -->
<string name="printing_disabled_by">Printing disabled by <xliff:g id="owner_app">%s</xliff:g>.</string>
@@ -764,7 +764,7 @@
<string name="capability_title_canCaptureFingerprintGestures">Fingerprint gestures</string>
<!-- Description for the capability of an accessibility service to perform gestures. -->
<string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on
- the device's fingerprint sensor.</string>
+ the device\'s fingerprint sensor.</string>
<!-- Permissions -->
@@ -3775,7 +3775,7 @@
<!-- Notification title when data usage has exceeded warning threshold. [CHAR LIMIT=50] -->
<string name="data_usage_warning_title">Data warning</string>
<!-- Notification body when data usage has exceeded warning threshold. [CHAR LIMIT=32] -->
- <string name="data_usage_warning_body">You've used <xliff:g id="app" example="3.8GB">%s</xliff:g> of data</string>
+ <string name="data_usage_warning_body">You\'ve used <xliff:g id="app" example="3.8GB">%s</xliff:g> of data</string>
<!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=50] -->
<string name="data_usage_mobile_limit_title">Mobile data limit reached</string>
@@ -3789,7 +3789,7 @@
<!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
<string name="data_usage_wifi_limit_snoozed_title">Over your Wi-Fi data limit</string>
<!-- Notification body when data usage has exceeded limit threshold. -->
- <string name="data_usage_limit_snoozed_body">You've gone <xliff:g id="size" example="3.8GB">%s</xliff:g> over your set limit</string>
+ <string name="data_usage_limit_snoozed_body">You\'ve gone <xliff:g id="size" example="3.8GB">%s</xliff:g> over your set limit</string>
<!-- Notification title when background data usage is limited. [CHAR LIMIT=32] -->
<string name="data_usage_restricted_title">Background data restricted</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1babd70..3b96861 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -218,6 +218,10 @@
<java-symbol type="id" name="selection_end_handle" />
<java-symbol type="id" name="insertion_handle" />
<java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
+ <java-symbol type="id" name="camera" />
+ <java-symbol type="id" name="mic" />
+ <java-symbol type="id" name="overlay" />
+ <java-symbol type="id" name="app_ops" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -1389,6 +1393,9 @@
<java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
<java-symbol type="drawable" name="autofilled_highlight"/>
+ <java-symbol type="drawable" name="ic_camera" />
+ <java-symbol type="drawable" name="ic_mic" />
+ <java-symbol type="drawable" name="ic_alert_window_layer" />
<java-symbol type="drawable" name="ic_account_circle" />
<java-symbol type="color" name="user_icon_1" />
@@ -3238,6 +3245,9 @@
<java-symbol type="string" name="keyguard_accessibility_sim_puk_unlock" />
<java-symbol type="string" name="keyguard_accessibility_password_unlock" />
+ <java-symbol type="dimen" name="status_bar_height_portrait" />
+ <java-symbol type="dimen" name="status_bar_height_landscape" />
+
<java-symbol type="string" name="global_action_logout" />
<java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="messaging_user" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 39310a8..cb11d8d 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -126,6 +126,7 @@
<item name="progressBarStyleInverse">@style/Widget.DeviceDefault.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.RatingBar.Indicator</item>
@@ -227,6 +228,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -247,6 +251,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -269,6 +276,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -290,6 +300,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -319,6 +332,9 @@
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -339,6 +355,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -358,6 +377,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -378,6 +400,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -414,6 +439,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -435,6 +463,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -454,6 +485,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -475,6 +509,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -495,6 +532,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -515,6 +555,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -535,6 +578,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -555,6 +601,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -575,6 +624,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
@@ -593,6 +645,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -611,6 +666,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -689,6 +747,7 @@
<item name="progressBarStyleInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.Light.RatingBar.Indicator</item>
@@ -785,6 +844,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -804,6 +866,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -824,6 +889,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -846,6 +914,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -867,6 +938,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -894,6 +968,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -914,6 +991,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -933,6 +1013,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -953,6 +1036,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -999,6 +1085,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -1020,6 +1109,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -1039,6 +1131,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -1060,6 +1155,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -1080,6 +1178,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -1098,6 +1199,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -1116,6 +1220,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -1147,6 +1254,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -1166,6 +1276,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -1175,6 +1288,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -1195,6 +1311,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -1214,6 +1333,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -1233,6 +1355,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -1252,6 +1377,9 @@
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -1278,6 +1406,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
</style>
<!-- DeviceDefault theme for the default system theme. -->
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 6ae0ef3..76d9ea6 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -259,6 +259,7 @@
<item name="progressBarStyleInverse">@style/Widget.Material.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.Material.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.Material.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/progress_bar_corner_material</item>
<item name="seekBarStyle">@style/Widget.Material.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.Material.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.Material.RatingBar.Indicator</item>
@@ -631,6 +632,7 @@
<item name="progressBarStyleInverse">@style/Widget.Material.Light.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@style/Widget.Material.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.Material.Light.ProgressBar.Large.Inverse</item>
+ <item name="progressBarCornerRadius">@dimen/progress_bar_corner_material</item>
<item name="seekBarStyle">@style/Widget.Material.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.Material.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.Material.Light.RatingBar.Indicator</item>
diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk
index 7d83527..f826ae9 100644
--- a/core/tests/BTtraffic/Android.mk
+++ b/core/tests/BTtraffic/Android.mk
@@ -9,6 +9,7 @@
$(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := bttraffic
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk
index 5ed93f3..8c0a330 100644
--- a/core/tests/ConnectivityManagerTest/Android.mk
+++ b/core/tests/ConnectivityManagerTest/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ConnectivityManagerTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk
index 2b80455..94ddccb 100644
--- a/core/tests/SvcMonitor/Android.mk
+++ b/core/tests/SvcMonitor/Android.mk
@@ -9,6 +9,7 @@
$(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := svcmonitor
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk
index ff9a0fe..dc80d00 100644
--- a/core/tests/bandwidthtests/Android.mk
+++ b/core/tests/bandwidthtests/Android.mk
@@ -29,6 +29,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := BandwidthTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk
index 744e5b0..bb4e302 100644
--- a/core/tests/bluetoothtests/Android.mk
+++ b/core/tests/bluetoothtests/Android.mk
@@ -11,6 +11,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := BluetoothTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 2ea1b46..2d25c7811 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -56,6 +56,7 @@
framework-atb-backward-compatibility \
LOCAL_PACKAGE_NAME := FrameworksCoreTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
index e31d50f..c3af6bd 100644
--- a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
index a63cf0e..34016ed 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/Android.mk
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BinderProxyCountingTestService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk
index 6280257..e04536b 100644
--- a/core/tests/coretests/BstatsTestApp/Android.mk
+++ b/core/tests/coretests/BstatsTestApp/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BstatsTestApp
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/core/tests/coretests/DisabledTestApp/Android.mk b/core/tests/coretests/DisabledTestApp/Android.mk
index a5daedf..e4304f7 100644
--- a/core/tests/coretests/DisabledTestApp/Android.mk
+++ b/core/tests/coretests/DisabledTestApp/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := DisabledTestApp
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/EnabledTestApp/Android.mk b/core/tests/coretests/EnabledTestApp/Android.mk
index 4b986d3..cd37f08 100644
--- a/core/tests/coretests/EnabledTestApp/Android.mk
+++ b/core/tests/coretests/EnabledTestApp/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := EnabledTestApp
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
index 1e03270..8a7d72a5 100644
--- a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
+++ b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
@@ -6,6 +6,7 @@
# Make sure every package name gets the FrameworkCoreTests_ prefix.
LOCAL_PACKAGE_NAME := FrameworkCoreTests_$(LOCAL_PACKAGE_NAME)
+LOCAL_SDK_VERSION := current
# Every package should have a native library
LOCAL_JNI_SHARED_LIBRARIES := libframeworks_coretests_jni
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index e575650..3eefc36 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -24,6 +24,9 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -48,6 +51,10 @@
import org.mockito.InOrder;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
/** Test {@link TransactionExecutor} logic. */
@RunWith(AndroidJUnit4.class)
@@ -56,6 +63,7 @@
public class TransactionExecutorTests {
private TransactionExecutor mExecutor;
+ private TransactionExecutorHelper mExecutorHelper;
private ClientTransactionHandler mTransactionHandler;
private ActivityClientRecord mClientRecord;
@@ -67,6 +75,7 @@
when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
mExecutor = spy(new TransactionExecutor(mTransactionHandler));
+ mExecutorHelper = new TransactionExecutorHelper();
}
@Test
@@ -166,10 +175,42 @@
pathExcludeLast(ON_DESTROY));
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleUndefinedStartState() {
+ mClientRecord.setState(UNDEFINED);
+ path(ON_CREATE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleUndefinedFinishState() {
+ mClientRecord.setState(PRE_ON_CREATE);
+ path(UNDEFINED);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidPreOnCreateFinishState() {
+ mClientRecord.setState(ON_CREATE);
+ path(PRE_ON_CREATE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidOnRestartStartState() {
+ mClientRecord.setState(ON_RESTART);
+ path(ON_RESUME);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLifecycleInvalidOnRestartFinishState() {
+ mClientRecord.setState(ON_CREATE);
+ path(ON_RESTART);
+ }
+
@Test
public void testTransactionResolution() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+ when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+ when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
IBinder token = mock(IBinder.class);
@@ -189,7 +230,7 @@
}
@Test
- public void testRequiredStateResolution() {
+ public void testActivityResultRequiredStateResolution() {
ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>());
IBinder token = mock(IBinder.class);
@@ -197,20 +238,161 @@
token /* activityToken */);
transaction.addCallback(activityResultItem);
+ // Verify resolution that should get to onPause
+ mClientRecord.setState(ON_RESUME);
mExecutor.executeCallbacks(transaction);
-
verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
+
+ // Verify resolution that should get to onStart
+ mClientRecord.setState(ON_STOP);
+ mExecutor.executeCallbacks(transaction);
+ verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START));
+ }
+
+ @Test
+ public void testClosestStateResolutionForSameState() {
+ final int[] allStates = new int[] {
+ ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
+
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_RESUME);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_STOP);
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord,
+ shuffledArray(allStates)));
+
+ mClientRecord.setState(PRE_ON_CREATE);
+ assertEquals(PRE_ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
+ new int[] {PRE_ON_CREATE}));
+ }
+
+ @Test
+ public void testClosestStateResolution() {
+ mClientRecord.setState(PRE_ON_CREATE);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_STOP, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_DESTROY})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnCreate() {
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnStart() {
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnResume() {
+ mClientRecord.setState(ON_RESUME);
+ assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnPause() {
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_STOP, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START})));
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnStop() {
+ mClientRecord.setState(ON_STOP);
+ assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
+ assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_DESTROY})));
+ assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE})));
+ }
+
+ @Test
+ public void testClosestStateResolutionFromOnDestroy() {
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP})));
+ }
+
+ @Test
+ public void testClosestStateResolutionToUndefined() {
+ mClientRecord.setState(ON_CREATE);
+ assertEquals(UNDEFINED,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, UNDEFINED));
+ }
+
+ @Test
+ public void testClosestStateResolutionToOnResume() {
+ mClientRecord.setState(ON_DESTROY);
+ assertEquals(ON_START,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ mClientRecord.setState(ON_START);
+ assertEquals(ON_START,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ mClientRecord.setState(ON_PAUSE);
+ assertEquals(ON_PAUSE,
+ mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
+ }
+
+ private static int[] shuffledArray(int[] inputArray) {
+ final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList());
+ Collections.shuffle(list);
+ return list.stream().mapToInt(Integer::intValue).toArray();
}
private int[] path(int finish) {
- mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
- false /* excludeLastState */);
- return mExecutor.getLifecycleSequence();
+ return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
+ false /* excludeLastState */).toArray();
}
private int[] pathExcludeLast(int finish) {
- mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
- true /* excludeLastState */);
- return mExecutor.getLifecycleSequence();
+ return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
+ true /* excludeLastState */).toArray();
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index f4d4c44..d6580d6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -314,6 +314,8 @@
Settings.Global.NTP_SERVER,
Settings.Global.NTP_TIMEOUT,
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
+ Settings.Global.OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
+ Settings.Global.OFF_BODY_RADIOS_OFF_DELAY_MS,
Settings.Global.OVERLAY_DISPLAY_DEVICES,
Settings.Global.PAC_CHANGE_DELAY,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ba9b963..b135025 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -38,10 +38,10 @@
public void testStandardActions_serializationFlagIsValid() {
AccessibilityAction brokenStandardAction = CollectionUtils.find(
new ArrayList<>(AccessibilityAction.sStandardActions),
- action -> Long.bitCount(action.mSerializationFlag) != 1);
+ action -> Integer.bitCount(action.mSerializationFlag) != 1);
if (brokenStandardAction != null) {
String message = "Invalid serialization flag(0x"
- + Long.toHexString(brokenStandardAction.mSerializationFlag)
+ + Integer.toHexString(brokenStandardAction.mSerializationFlag)
+ ") in " + brokenStandardAction;
if (brokenStandardAction.mSerializationFlag == 0L) {
message += "\nThis is likely due to an overflow";
@@ -56,7 +56,7 @@
&& action.getId() != action.mSerializationFlag);
if (brokenStandardAction != null) {
fail("Serialization flag(0x"
- + Long.toHexString(brokenStandardAction.mSerializationFlag)
+ + Integer.toHexString(brokenStandardAction.mSerializationFlag)
+ ") is different from legacy action id(0x"
+ Integer.toHexString(brokenStandardAction.getId())
+ ") in " + brokenStandardAction);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
new file mode 100644
index 0000000..7f16359
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationConstantsTest {
+
+ @Test
+ public void testLoadFromString() {
+ final String s = "model_dark_launch_enabled=true,"
+ + "smart_selection_enabled=true,"
+ + "smart_text_share_enabled=true,"
+ + "smart_linkify_enabled=true,"
+ + "smart_select_animation_enabled=true,"
+ + "suggest_selection_max_range_length=10,"
+ + "classify_text_max_range_length=11,"
+ + "generate_links_max_text_length=12,"
+ + "generate_links_log_sample_rate=13";
+ final TextClassificationConstants constants =
+ TextClassificationConstants.loadFromString(s);
+ assertTrue("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+ assertTrue("smart_selection_enabled", constants.isSmartSelectionEnabled());
+ assertTrue("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+ assertTrue("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+ assertTrue("smart_select_animation_enabled", constants.isSmartSelectionAnimationEnabled());
+ assertEquals("suggest_selection_max_range_length",
+ 10, constants.getSuggestSelectionMaxRangeLength());
+ assertEquals("classify_text_max_range_length",
+ 11, constants.getClassifyTextMaxRangeLength());
+ assertEquals("generate_links_max_text_length",
+ 12, constants.getGenerateLinksMaxTextLength());
+ assertEquals("generate_links_log_sample_rate",
+ 13, constants.getGenerateLinksLogSampleRate());
+ }
+
+ @Test
+ public void testLoadFromString_differentValues() {
+ final String s = "model_dark_launch_enabled=false,"
+ + "smart_selection_enabled=false,"
+ + "smart_text_share_enabled=false,"
+ + "smart_linkify_enabled=false,"
+ + "smart_select_animation_enabled=false,"
+ + "suggest_selection_max_range_length=8,"
+ + "classify_text_max_range_length=7,"
+ + "generate_links_max_text_length=6,"
+ + "generate_links_log_sample_rate=5";
+ final TextClassificationConstants constants =
+ TextClassificationConstants.loadFromString(s);
+ assertFalse("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+ assertFalse("smart_selection_enabled", constants.isSmartSelectionEnabled());
+ assertFalse("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+ assertFalse("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+ assertFalse("smart_select_animation_enabled",
+ constants.isSmartSelectionAnimationEnabled());
+ assertEquals("suggest_selection_max_range_length",
+ 8, constants.getSuggestSelectionMaxRangeLength());
+ assertEquals("classify_text_max_range_length",
+ 7, constants.getClassifyTextMaxRangeLength());
+ assertEquals("generate_links_max_text_length",
+ 6, constants.getGenerateLinksMaxTextLength());
+ assertEquals("generate_links_log_sample_rate",
+ 5, constants.getGenerateLinksLogSampleRate());
+ }
+
+ @Test
+ public void testEntityListParsing() {
+ final TextClassificationConstants constants = TextClassificationConstants.loadFromString(
+ "entity_list_default=phone,"
+ + "entity_list_not_editable=address:flight,"
+ + "entity_list_editable=date:datetime");
+ assertEquals(1, constants.getEntityListDefault().size());
+ assertEquals("phone", constants.getEntityListDefault().get(0));
+ assertEquals(2, constants.getEntityListNotEditable().size());
+ assertEquals("address", constants.getEntityListNotEditable().get(0));
+ assertEquals("flight", constants.getEntityListNotEditable().get(1));
+ assertEquals(2, constants.getEntityListEditable().size());
+ assertEquals("date", constants.getEntityListEditable().get(0));
+ assertEquals("datetime", constants.getEntityListEditable().get(1));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
deleted file mode 100644
index 984eede..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static org.junit.Assert.assertEquals;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierConstantsTest {
-
- @Test
- public void testEntityListParsing() {
- final TextClassifierConstants constants = TextClassifierConstants.loadFromString(
- "entity_list_default=phone,"
- + "entity_list_not_editable=address:flight,"
- + "entity_list_editable=date:datetime");
- assertEquals(1, constants.getEntityListDefault().size());
- assertEquals("phone", constants.getEntityListDefault().get(0));
- assertEquals(2, constants.getEntityListNotEditable().size());
- assertEquals("address", constants.getEntityListNotEditable().get(0));
- assertEquals("flight", constants.getEntityListNotEditable().get(1));
- assertEquals(2, constants.getEntityListEditable().size());
- assertEquals("date", constants.getEntityListEditable().get(0));
- assertEquals("datetime", constants.getEntityListEditable().get(1));
- }
-}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 1d9f624..cba9cbd 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -24,6 +24,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := DownloadManagerTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
ifneq ($(TARGET_BUILD_VARIANT),user)
# Need to run as system app to get access to Settings. This test won't work for user builds.
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index 30c2dca..73ee8b8 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -10,6 +10,9 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := NotificationStressTests
+# Could build against SDK if it wasn't for the @RepetitiveTest annotation.
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
LOCAL_STATIC_JAVA_LIBRARIES := \
junit \
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
index 4ca3e4c..680ebeb 100644
--- a/core/tests/overlaytests/device/Android.mk
+++ b/core/tests/overlaytests/device/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayDeviceTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_TARGET_REQUIRED_MODULES := \
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
index 17e20ee..edad4b2 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
@@ -17,6 +17,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
index c24bea9..3fae8e1 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
@@ -17,6 +17,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
index dc811c5..c352c05 100644
--- a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
@@ -17,6 +17,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayDeviceTests_FrameworkOverlay
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
index 4249549..3d2410d 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
@@ -19,6 +19,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_BadSignatureOverlay
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad
include $(BUILD_PACKAGE)
@@ -26,6 +27,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_static
@@ -34,6 +36,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index d26425b..ab3faf0 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -18,6 +18,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
include $(BUILD_PACKAGE)
@@ -27,6 +28,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
@@ -38,6 +40,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
@@ -51,6 +54,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
@@ -62,6 +66,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index 5bfde78..f95231f 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -15,6 +15,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 7bba417..374d0d0 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -12,6 +12,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index 57e2059..3458be0 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -12,6 +12,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 2dc1059..5c60c81 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -22,6 +22,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
LOCAL_PACKAGE_NAME := FrameworksUtilTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java
index a9e180f..003678a 100644
--- a/graphics/java/android/graphics/EmbossMaskFilter.java
+++ b/graphics/java/android/graphics/EmbossMaskFilter.java
@@ -20,12 +20,15 @@
/**
* Create an emboss maskfilter
*
+ * @deprecated This subclass is not supported and should not be instantiated.
+ *
* @param direction array of 3 scalars [x, y, z] specifying the direction of the light source
* @param ambient 0...1 amount of ambient light
* @param specular coefficient for specular highlights (e.g. 8)
* @param blurRadius amount to blur before applying lighting (e.g. 3)
* @return the emboss maskfilter
*/
+ @Deprecated
public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) {
if (direction.length < 3) {
throw new ArrayIndexOutOfBoundsException();
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index b6ffe12..5abd31a 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -19,6 +19,8 @@
import static android.system.OsConstants.SEEK_CUR;
import static android.system.OsConstants.SEEK_SET;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,32 +30,28 @@
import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.net.Uri;
-import android.util.Size;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
+import android.util.Size;
import android.util.TypedValue;
-import libcore.io.IoUtils;
import dalvik.system.CloseGuard;
-import java.nio.ByteBuffer;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.lang.AutoCloseable;
-import java.lang.NullPointerException;
-import java.lang.RuntimeException;
import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -494,7 +492,7 @@
private int mAllocator = ALLOCATOR_DEFAULT;
private boolean mRequireUnpremultiplied = false;
private boolean mMutable = false;
- private boolean mPreferRamOverQuality = false;
+ private boolean mConserveMemory = false;
private boolean mAsAlphaMask = false;
private Rect mCropRect;
private Rect mOutPaddingRect;
@@ -623,16 +621,18 @@
/**
* Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
*
- * <p>The returned {@link Source} effectively takes ownership of the
- * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
- * this call.</p>
+ * <p>Decoding will start from {@link java.nio.ByteBuffer#position()}. The
+ * position of {@code buffer} will not be affected.</p>
*
- * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
- * position after decoding is undefined.
+ * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable}, and
+ * the encoded image is animated, the returned {@link AnimatedImageDrawable}
+ * will continue reading from the {@code buffer}, so its contents must not
+ * be modified, even after the {@code AnimatedImageDrawable} is returned.
+ * {@code buffer}'s contents should never be modified during decode.</p>
*/
@NonNull
public static Source createSource(@NonNull ByteBuffer buffer) {
- return new ByteBufferSource(buffer);
+ return new ByteBufferSource(buffer.slice());
}
/**
@@ -692,8 +692,9 @@
*
* @param width must be greater than 0.
* @param height must be greater than 0.
+ * @return this object for chaining.
*/
- public void setResize(int width, int height) {
+ public ImageDecoder setResize(int width, int height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Dimensions must be positive! "
+ "provided (" + width + ", " + height + ")");
@@ -701,6 +702,7 @@
mDesiredWidth = width;
mDesiredHeight = height;
+ return this;
}
/**
@@ -710,10 +712,11 @@
* {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
*
* @param sampleSize Sampling rate of the encoded image.
+ * @return this object for chaining.
*/
- public void setResize(int sampleSize) {
+ public ImageDecoder setResize(int sampleSize) {
Size size = this.getSampledSize(sampleSize);
- this.setResize(size.getWidth(), size.getHeight());
+ return this.setResize(size.getWidth(), size.getHeight());
}
private boolean requestedResize() {
@@ -769,18 +772,20 @@
* This is ignored for animated drawables.
*
* @param allocator Type of allocator to use.
+ * @return this object for chaining.
*/
- public void setAllocator(@Allocator int allocator) {
+ public ImageDecoder setAllocator(@Allocator int allocator) {
if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
throw new IllegalArgumentException("invalid allocator " + allocator);
}
mAllocator = allocator;
+ return this;
}
/**
* Specify whether the {@link Bitmap} should have unpremultiplied pixels.
*
- * By default, ImageDecoder will create a {@link Bitmap} with
+ * <p>By default, ImageDecoder will create a {@link Bitmap} with
* premultiplied pixels, which is required for drawing with the
* {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
* this method with a value of {@code true} will result in
@@ -788,9 +793,13 @@
* pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
* {@link #decodeDrawable}; attempting to decode an unpremultiplied
* {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
+ * </p>
+ *
+ * @return this object for chaining.
*/
- public void setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+ public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
mRequireUnpremultiplied = requireUnpremultiplied;
+ return this;
}
/**
@@ -805,19 +814,25 @@
* <p>For an animated image, the drawing commands drawn on the
* {@link Canvas} will be recorded immediately and then applied to each
* frame.</p>
+ *
+ * @return this object for chaining.
*/
- public void setPostProcessor(@Nullable PostProcessor p) {
+ public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
mPostProcessor = p;
+ return this;
}
/**
* Set (replace) the {@link OnPartialImageListener} on this object.
*
- * Will be called if there is an error in the input. Without one, a
- * partial {@link Bitmap} will be created.
+ * <p>Will be called if there is an error in the input. Without one, an
+ * error will result in an Exception being thrown.</p>
+ *
+ * @return this object for chaining.
*/
- public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+ public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
mOnPartialImageListener = l;
+ return this;
}
/**
@@ -831,9 +846,12 @@
* <p>NOT intended as a replacement for
* {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
* but merely crops the output.</p>
+ *
+ * @return this object for chaining.
*/
- public void setCrop(@Nullable Rect subset) {
+ public ImageDecoder setCrop(@Nullable Rect subset) {
mCropRect = subset;
+ return this;
}
/**
@@ -842,10 +860,13 @@
* If the image is a nine patch, this Rect will be set to the padding
* rectangle during decode. Otherwise it will not be modified.
*
+ * @return this object for chaining.
+ *
* @hide
*/
- public void setOutPaddingRect(@NonNull Rect outPadding) {
+ public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
mOutPaddingRect = outPadding;
+ return this;
}
/**
@@ -863,21 +884,31 @@
* which would require retrieving the Bitmap from the returned Drawable in
* order to modify. Attempting to decode a mutable {@link Drawable} will
* throw an {@link java.lang.IllegalStateException}.</p>
+ *
+ * @return this object for chaining.
*/
- public void setMutable(boolean mutable) {
+ public ImageDecoder setMutable(boolean mutable) {
mMutable = mutable;
+ return this;
}
/**
* Specify whether to potentially save RAM at the expense of quality.
*
- * Setting this to {@code true} may result in a {@link Bitmap} with a
- * denser {@link Bitmap.Config}, depending on the image. For example, for
- * an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
- * with no alpha information.
+ * <p>Setting this to {@code true} may result in a {@link Bitmap} with a
+ * denser {@link Bitmap.Config}, depending on the image. For example, an
+ * opaque {@link Bitmap} with 8 bits or precision for each of its red,
+ * green and blue components would decode to
+ * {@link Bitmap.Config#ARGB_8888} by default, but setting this to
+ * {@code true} will result in decoding to {@link Bitmap.Config#RGB_565}.
+ * This necessarily lowers the quality of the output, but saves half
+ * the memory used.</p>
+ *
+ * @return this object for chaining.
*/
- public void setPreferRamOverQuality(boolean preferRamOverQuality) {
- mPreferRamOverQuality = preferRamOverQuality;
+ public ImageDecoder setConserveMemory(boolean conserveMemory) {
+ mConserveMemory = conserveMemory;
+ return this;
}
/**
@@ -891,9 +922,12 @@
* combine them will result in {@link #decodeDrawable}/
* {@link #decodeBitmap} throwing an
* {@link java.lang.IllegalStateException}.</p>
+ *
+ * @return this object for chaining.
*/
- public void setAsAlphaMask(boolean asAlphaMask) {
+ public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
mAsAlphaMask = asAlphaMask;
+ return this;
}
@Override
@@ -958,7 +992,7 @@
return nDecodeBitmap(mNativePtr, partialImagePtr,
postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
- mPreferRamOverQuality, mAsAlphaMask);
+ mConserveMemory, mAsAlphaMask);
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1172,7 +1206,7 @@
int width, int height,
@Nullable Rect cropRect, boolean mutable,
int allocator, boolean requireUnpremul,
- boolean preferRamOverQuality, boolean asAlphaMask)
+ boolean conserveMemory, boolean asAlphaMask)
throws IOException;
private static native Size nGetSampledSize(long nativePtr,
int sampleSize);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 8595165..38beebd 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -21,6 +21,7 @@
import static android.content.res.FontResourcesParser.FontFileResourceEntry;
import static android.content.res.FontResourcesParser.ProviderResourceEntry;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -49,6 +50,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
@@ -117,6 +120,11 @@
*/
public long native_instance;
+ /** @hide */
+ @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Style {}
+
// Style
public static final int NORMAL = 0;
public static final int BOLD = 1;
@@ -124,8 +132,15 @@
public static final int BOLD_ITALIC = 3;
/** @hide */ public static final int STYLE_MASK = 0x03;
- private int mStyle = 0;
- private int mWeight = 0;
+ private @Style int mStyle = 0;
+
+ /**
+ * A maximum value for the weight value.
+ * @hide
+ */
+ public static final int MAX_WEIGHT = 1000;
+
+ private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
// Value for weight and italic. Indicates the value is resolved by font metadata.
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -153,7 +168,7 @@
}
/** Returns the typeface's intrinsic style attributes */
- public int getStyle() {
+ public @Style int getStyle() {
return mStyle;
}
@@ -659,7 +674,7 @@
* e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
* @return The best matching typeface.
*/
- public static Typeface create(String familyName, int style) {
+ public static Typeface create(String familyName, @Style int style) {
return create(sSystemFontMap.get(familyName), style);
}
@@ -680,7 +695,7 @@
* e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
* @return The best matching typeface.
*/
- public static Typeface create(Typeface family, int style) {
+ public static Typeface create(Typeface family, @Style int style) {
if ((style & ~STYLE_MASK) != 0) {
style = NORMAL;
}
@@ -776,7 +791,7 @@
*
* @return the default typeface that corresponds to the style
*/
- public static Typeface defaultFromStyle(int style) {
+ public static Typeface defaultFromStyle(@Style int style) {
return sDefaults[style];
}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 749b7594..361fe0b 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -18,11 +18,14 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
-import android.content.res.ColorStateList;
+import android.annotation.IdRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -60,17 +63,40 @@
public final class Icon implements Parcelable {
private static final String TAG = "Icon";
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
+ * @see #getType
+ */
public static final int TYPE_BITMAP = 1;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithResource}.
+ * @see #getType
+ */
public static final int TYPE_RESOURCE = 2;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithData(byte[], int, int)}.
+ * @see #getType
+ */
public static final int TYPE_DATA = 3;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithContentUri}
+ * or {@link Icon#createWithFilePath(String)}.
+ * @see #getType
+ */
public static final int TYPE_URI = 4;
- /** @hide */
+ /**
+ * An icon that was created using {@link Icon#createWithAdaptiveBitmap}.
+ * @see #getType
+ */
public static final int TYPE_ADAPTIVE_BITMAP = 5;
+ /**
+ * @hide
+ */
+ @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+ public @interface IconType {
+ }
+
private static final int VERSION_STREAM_SERIALIZER = 1;
private final int mType;
@@ -99,14 +125,12 @@
private int mInt2;
/**
- * @return The type of image data held in this Icon. One of
- * {@link #TYPE_BITMAP},
- * {@link #TYPE_RESOURCE},
- * {@link #TYPE_DATA}, or
- * {@link #TYPE_URI}.
- * {@link #TYPE_ADAPTIVE_BITMAP}
- * @hide
+ * Gets the type of the icon provided.
+ * <p>
+ * Note that new types may be added later, so callers should guard against other
+ * types being returned.
*/
+ @IconType
public int getType() {
return mType;
}
@@ -179,9 +203,13 @@
}
/**
- * @return The package containing resources for this {@link #TYPE_RESOURCE} Icon.
- * @hide
+ * Gets the package used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_RESOURCE}.
+ * Note: This package may not be available if referenced in the future, and it is
+ * up to the caller to ensure safety if this package is re-used and/or persisted.
*/
+ @NonNull
public String getResPackage() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResPackage() on " + this);
@@ -190,9 +218,13 @@
}
/**
- * @return The resource ID for this {@link #TYPE_RESOURCE} Icon.
- * @hide
+ * Gets the resource used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_RESOURCE}.
+ * Note: This resource may not be available if the application changes at all, and it is
+ * up to the caller to ensure safety if this resource is re-used and/or persisted.
*/
+ @IdRes
public int getResId() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResId() on " + this);
@@ -212,9 +244,13 @@
}
/**
- * @return The {@link android.net.Uri} for this {@link #TYPE_URI} Icon.
- * @hide
+ * Gets the uri used to create this icon.
+ * <p>
+ * Only valid for icons of type {@link #TYPE_URI}.
+ * Note: This uri may not be available in the future, and it is
+ * up to the caller to ensure safety if this uri is re-used and/or persisted.
*/
+ @NonNull
public Uri getUri() {
return Uri.parse(getUriString());
}
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
index 1167f76..596e5f5 100644
--- a/keystore/tests/Android.mk
+++ b/keystore/tests/Android.mk
@@ -24,6 +24,7 @@
android-support-test
LOCAL_PACKAGE_NAME := KeystoreTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 247458d..c512a6b 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -26,7 +26,7 @@
#include <androidfw/Util.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
#include <utils/FileMap.h>
#include <utils/Log.h>
#include <utils/threads.h>
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index a5698af..fc625bb 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -28,7 +28,7 @@
#include <androidfw/misc.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/ZipFileRO.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/String8.h>
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 696a00c..6268c40 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -32,7 +32,7 @@
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
#include <utils/ByteOrder.h>
#include <utils/Debug.h>
#include <utils/Log.h>
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index 49bfe1e..ef6a8ff 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -40,6 +40,9 @@
// incidentd calls logs for annotated field
SECTION_LOG = 4;
+
+ // incidentd read file and gzip the data in bytes field
+ SECTION_GZIP = 5;
}
message SectionFlags {
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index 0a8a5aa..bf698d4 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -154,7 +154,7 @@
void editRawFixed32(size_t pos, uint32_t val);
/**
- * Copy _size_ bytes of data starting at __srcPos__ to wp.
+ * Copy _size_ bytes of data starting at __srcPos__ to wp, srcPos must be larger than wp.pos().
*/
void copy(size_t srcPos, size_t size);
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 44d290e..b2fd8ec 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -9,6 +9,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := FrameworksLocationTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index 99499dc..6fd47c6 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -59,6 +59,7 @@
android.test.base \
LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index fbe5561..6dd4f69 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -19,7 +19,9 @@
import java.nio.ByteBuffer;
import java.lang.AutoCloseable;
+import android.annotation.Nullable;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
/**
* <p>A single complete image buffer to use with a media source such as a
@@ -184,6 +186,23 @@
public abstract long getTimestamp();
/**
+ * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
+ * intended for GPU and/or hardware access.
+ * <p>
+ * The returned {@link android.hardware.HardwareBuffer HardwareBuffer} shall not be used
+ * after {@link Image#close Image.close()} has been called.
+ * </p>
+ * @return the HardwareBuffer associated with this Image or null if this Image doesn't support
+ * this feature (e.g. {@link android.media.ImageWriter ImageWriter} or
+ * {@link android.media.MediaCodec MediaCodec} don't).
+ */
+ @Nullable
+ public HardwareBuffer getHardwareBuffer() {
+ throwISEIfImageIsInvalid();
+ return null;
+ }
+
+ /**
* Set the timestamp associated with this frame.
* <p>
* The timestamp is measured in nanoseconds, and is normally monotonically
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 1019580..fb0de5c 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -876,6 +876,12 @@
}
@Override
+ public HardwareBuffer getHardwareBuffer() {
+ throwISEIfImageIsInvalid();
+ return nativeGetHardwareBuffer();
+ }
+
+ @Override
public void setTimestamp(long timestampNs) {
throwISEIfImageIsInvalid();
mTimestamp = timestampNs;
@@ -1017,6 +1023,7 @@
private synchronized native int nativeGetWidth();
private synchronized native int nativeGetHeight();
private synchronized native int nativeGetFormat(int readerFormat);
+ private synchronized native HardwareBuffer nativeGetHardwareBuffer();
}
private synchronized native void nativeInit(Object weakSelf, int w, int h,
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 0114240..c99a19b 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -371,7 +371,7 @@
* Request that the player prepare its playback. In other words, other sessions can continue
* to play during the preparation of this session. This method can be used to speed up the
* start of the playback. Once the preparation is done, the session will change its playback
- * state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+ * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called to
* start playback.
*/
public void prepare() {
@@ -479,7 +479,7 @@
* Request that the player prepare playback for a specific media id. In other words, other
* sessions can continue to play during the preparation of this session. This method can be
* used to speed up the start of the playback. Once the preparation is done, the session
- * will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
+ * will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromMediaId} can be directly called without this method.
*
@@ -496,7 +496,7 @@
* query should be treated as a request to prepare any music. In other words, other sessions
* can continue to play during the preparation of this session. This method can be used to
* speed up the start of the playback. Once the preparation is done, the session will
- * change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
+ * change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromSearch} can be directly called without this method.
*
@@ -512,7 +512,7 @@
* Request that the player prepare playback for a specific {@link Uri}. In other words,
* other sessions can continue to play during the preparation of this session. This method
* can be used to speed up the start of the playback. Once the preparation is done, the
- * session will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
+ * session will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
* {@link #play} can be called to start playback. If the preparation is not needed,
* {@link #playFromUri} can be directly called without this method.
*
@@ -698,13 +698,10 @@
}
/**
- * Removes the media item at index in the play list.
+ * Removes the media item at index in the playlist.
*<p>
* If index is same as the current index of the playlist, current playback
* will be stopped and playback moves to next source in the list.
- *
- * @throws IllegalArgumentException if the play list is null
- * @throws IndexOutOfBoundsException if index is outside play list range
*/
@Override
public void removePlaylistItem(@NonNull MediaItem2 item) {
@@ -712,6 +709,16 @@
}
/**
+ * Replace the media item at index in the playlist.
+ * @param index the index of the item to replace
+ * @param item the new item
+ */
+ @Override
+ public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+ mProvider.replacePlaylistItem_impl(index, item);
+ }
+
+ /**
* Inserts the media item to the play list at position index.
* <p>
* This will not change the currently playing media item.
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 3181362..bc4ae7a 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -34,151 +34,133 @@
/**
* @hide
*/
- @IntDef({STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_ERROR})
+ @IntDef({
+ PLAYER_STATE_IDLE,
+ PLAYER_STATE_PAUSED,
+ PLAYER_STATE_PLAYING,
+ PLAYER_STATE_ERROR })
@Retention(RetentionPolicy.SOURCE)
- public @interface State {}
+ public @interface PlayerState {}
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ BUFFERING_STATE_UNKNOWN,
+ BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
+ BUFFERING_STATE_BUFFERING_AND_STARVED,
+ BUFFERING_STATE_BUFFERING_COMPLETE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BuffState {}
/**
* State when the player is idle, and needs configuration to start playback.
*/
- public static final int STATE_IDLE = 0;
+ public static final int PLAYER_STATE_IDLE = 0;
/**
* State when the player's playback is paused
*/
- public static final int STATE_PAUSED = 0;
+ public static final int PLAYER_STATE_PAUSED = 1;
/**
* State when the player's playback is ongoing
*/
- public static final int STATE_PLAYING = 0;
+ public static final int PLAYER_STATE_PLAYING = 2;
/**
* State when the player is in error state and cannot be recovered self.
*/
- public static final int STATE_ERROR = 0;
+ public static final int PLAYER_STATE_ERROR = 3;
/**
- * Unspecified media player error.
- * @hide
+ * Buffering state is unknown.
*/
- public static final int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
+ public static final int BUFFERING_STATE_UNKNOWN = 0;
/**
- * The video is streamed and its container is not valid for progressive
- * playback i.e the video's index (e.g moov atom) is not at the start of the
- * file.
- * @hide
+ * Buffering state indicating the player is buffering but enough has been buffered
+ * for this player to be able to play the content.
+ * See {@link #getBufferedPosition()} for how far is buffered already.
*/
- public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
- MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
+ public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
/**
- * File or network related operation errors.
- * @hide
+ * Buffering state indicating the player is buffering, but the player is currently starved
+ * for data, and cannot play.
*/
- public static final int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
+ public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
/**
- * Bitstream is not conforming to the related coding standard or file spec.
- * @hide
+ * Buffering state indicating the player is done buffering, and the remainder of the content is
+ * available for playback.
*/
- public static final int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
+ public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
/**
- * Bitstream is conforming to the related coding standard or file spec, but
- * the media framework does not support the feature.
- * @hide
- */
- public static final int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
- /**
- * Some operation takes too long to complete, usually more than 3-5 seconds.
- * @hide
- */
- public static final int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
-
- /**
- * Callbacks to listens to the changes in {@link PlaybackState2} and error.
- * @hide
- */
- public static abstract class EventCallback {
- /**
- * Called when {@link PlaybackState2} for this player is changed.
- */
- public void onPlaybackStateChanged(PlaybackState2 state) { }
-
- /**
- * Called to indicate an error.
- *
- * @param mediaId optional mediaId to indicate error
- * @param what what
- * @param extra
- */
- public void onError(@Nullable String mediaId, int what, int extra) { }
- }
-
- // Transport controls that session will send command directly to this player.
- /**
- * Start or resumes playback
+ * Starts or resumes playback.
*/
public abstract void play();
/**
- * @hide
+ * Prepares the player for playback.
+ * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
+ * notified when the preparation phase completed. During this time, the player may allocate
+ * resources required to play, such as audio and video decoders.
*/
public abstract void prepare();
/**
- * Pause playback
+ * Pauses playback.
*/
public abstract void pause();
/**
- * @hide
- */
- public abstract void stop();
-
- /**
- * @hide
- */
- public abstract void skipToPrevious();
-
- /**
- * @hide
+ *
*/
public abstract void skipToNext();
/**
- * @hide
+ * Moves the playback head to the specified position
+ * @param pos the new playback position expressed in ms.
*/
public abstract void seekTo(long pos);
- /**
- * @hide
- */
- public abstract void fastForward();
+ public static final long UNKNOWN_TIME = -1;
/**
- * @hide
+ * Returns the current playback head position.
+ * @return the current play position in ms, or {@link #UNKNOWN_TIME} if unknown.
*/
- public abstract void rewind();
+ public long getCurrentPosition() { return UNKNOWN_TIME; }
/**
- * @hide
+ * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
+ * @return the duration in ms, or {@link #UNKNOWN_TIME}.
*/
- public abstract PlaybackState2 getPlaybackState();
+ public long getDuration() { return UNKNOWN_TIME; }
/**
- * Return player state.
- *
- * @return player state
- * @see #STATE_IDLE
- * @see #STATE_PLAYING
- * @see #STATE_PAUSED
- * @see #STATE_ERROR
+ * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
+ * @return the duration in ms, or {@link #UNKNOWN_TIME}.
*/
- public abstract @State int getPlayerState();
+ public long getBufferedPosition() { return UNKNOWN_TIME; }
+
+ /**
+ * Returns the current player state.
+ * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
+ * notification of changes.
+ * @return the current player state
+ */
+ public abstract @PlayerState int getPlayerState();
+
+ /**
+ * Returns the current buffering state of the player.
+ * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+ * buffered.
+ * @return the buffering state.
+ */
+ public abstract @BuffState int getBufferingState();
/**
* Sets the {@link AudioAttributes} to be used during the playback of the media.
@@ -193,55 +175,136 @@
public abstract @Nullable AudioAttributes getAudioAttributes();
/**
- * @hide
+ * Sets the data source to be played.
+ * @param dsd
*/
- public abstract void addPlaylistItem(int index, MediaItem2 item);
+ public abstract void setDataSource(@NonNull DataSourceDesc dsd);
/**
- * @hide
+ * Sets the data source that will be played immediately after the current one is done playing.
+ * @param dsd
*/
- public abstract void removePlaylistItem(MediaItem2 item);
+ public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
/**
- * @hide
+ * Sets the list of data sources that will be sequentially played after the current one. Each
+ * data source is played immediately after the previous one is done playing.
+ * @param dsds
*/
- public abstract void setPlaylist(List<MediaItem2> playlist);
+ public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
/**
- * @hide
+ * Returns the current data source.
+ * @return the current data source, or null if none is set, or none available to play.
*/
- public abstract List<MediaItem2> getPlaylist();
+ public abstract @Nullable DataSourceDesc getCurrentDataSource();
/**
- * @hide
+ * Configures the player to loop on the current data source.
+ * @param loop true if the current data source is meant to loop.
*/
- public abstract void setCurrentPlaylistItem(MediaItem2 item);
+ public abstract void loopCurrent(boolean loop);
/**
- * @hide
+ * Sets the playback speed.
+ * A value of 1.0f is the default playback value.
+ * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+ * before using negative values.<br>
+ * After changing the playback speed, it is recommended to query the actual speed supported
+ * by the player, see {@link #getPlaybackSpeed()}.
+ * @param speed
*/
- public abstract void setPlaylistParams(PlaylistParams params);
+ public abstract void setPlaybackSpeed(float speed);
/**
- * @hide
+ * Returns the actual playback speed to be used by the player when playing.
+ * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+ * @return the actual playback speed
*/
- public abstract PlaylistParams getPlaylistParams();
+ public float getPlaybackSpeed() { return 1.0f; }
/**
- * Register a {@link EventCallback}.
- *
- * @param executor a callback executor
- * @param callback a EventCallback
- * @hide
+ * Indicates whether reverse playback is supported.
+ * Reverse playback is indicated by negative playback speeds, see
+ * {@link #setPlaybackSpeed(float)}.
+ * @return true if reverse playback is supported.
*/
- public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback callback);
+ public boolean isReversePlaybackSupported() { return false; }
/**
- * Unregister previously registered {@link EventCallback}.
- *
- * @param callback a EventCallback
- * @hide
+ * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+ * on the audio samples.
+ * Note that this volume is specific to the player, and is separate from stream volume
+ * used across the platform.<br>
+ * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+ * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+ * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
*/
- public abstract void unregisterEventCallback(@NonNull EventCallback callback);
+ public abstract void setVolume(float volume);
+
+ /**
+ * Returns the current volume of this player to this player.
+ * Note that it does not take into account the associated stream volume.
+ * @return the player volume.
+ */
+ public abstract float getPlayerVolume();
+
+ /**
+ * @return the maximum volume that can be used in {@link #setVolume(float)}.
+ */
+ public float getMaxPlayerVolume() { return 1.0f; }
+
+ /**
+ * Adds a callback to be notified of events for this player.
+ * @param e the {@link Executor} to be used for the events.
+ * @param cb the callback to receive the events.
+ */
+ public abstract void registerPlayerEventCallback(@NonNull Executor e,
+ @NonNull PlayerEventCallback cb);
+
+ /**
+ * Removes a previously registered callback for player events
+ * @param cb the callback to remove
+ */
+ public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
+
+ /**
+ * A callback class to receive notifications for events on the media player.
+ * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
+ * register this callback.
+ */
+ public static abstract class PlayerEventCallback {
+ /**
+ * Called when the player's curretn data source has changed.
+ * @param mpb the player whose data source changed.
+ * @param dsd the new current data source.
+ */
+ public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
+ @Nullable DataSourceDesc dsd) { }
+ /**
+ * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
+ * referenced by the given data source.
+ * @param mpb the player that is prepared.
+ * @param dsd the data source that the player is prepared to play.
+ */
+ public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
+
+ /**
+ * Called to indicate that the state of the player has changed.
+ * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
+ * @param mpb the player whose state has changed.
+ * @param state the new state of the player.
+ */
+ public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
+
+ /**
+ * Called to report buffering events for a data source.
+ * @param mpb the player that is buffering
+ * @param dsd the data source for which buffering is happening.
+ * @param state the new buffering state.
+ */
+ public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
+ @NonNull DataSourceDesc dsd, @BuffState int state) { }
+ }
+
}
diff --git a/media/java/android/media/MediaPlaylistController.java b/media/java/android/media/MediaPlaylistController.java
index 916c12a..c98d50e 100644
--- a/media/java/android/media/MediaPlaylistController.java
+++ b/media/java/android/media/MediaPlaylistController.java
@@ -21,21 +21,19 @@
import java.util.List;
/**
- * Controller interfaces for playlist management for both {@link MediaSession2} and
- * {@link MediaController2} that related with metadata. This ensures that two classes share the same
- * interface.
- * <p>
- * This class only includes methods that involves {@link MediaItem2}. Because other APIs are
- * considered as the part of {@link MediaPlayerBase} (e.g. set/getPlaylistParams()}. Note that
- * setPlaylist() isn't added on purpose because it's considered as session specific.
- *
- * @hide
+ * Controller interface for playlist management.
+ * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
+ * and data sources (as {@link DataSourceDesc})
+ * Used by {@link MediaSession2} and {@link MediaController2}.
*/
+ // This class only includes methods that contain {@link MediaItem2}.
+ // Note that setPlaylist() isn't added on purpose because it's considered session-specific.
+
public interface MediaPlaylistController {
- // TODO(jaewan): is Index correct here?
void addPlaylistItem(int index, @NonNull MediaItem2 item);
void removePlaylistItem(@NonNull MediaItem2 item);
MediaItem2 getCurrentPlaylistItem();
void skipToPlaylistItem(@NonNull MediaItem2 item);
+ void replacePlaylistItem(int index, @NonNull MediaItem2 item);
List<MediaItem2> getPlaylist();
}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 54b1f0e..7bfaeef 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -23,7 +23,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase.PlayerEventCallback;
import android.media.session.MediaSession;
import android.media.session.MediaSession.Callback;
import android.media.session.PlaybackState;
@@ -764,6 +764,16 @@
context, (Builder) instance, player));
}
+ public Builder(Context context, @NonNull MediaPlayerBase player,
+ @NonNull MediaPlaylistController mplc) {
+ //TODO use the MediaPlaylistController
+ super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
+ context, (Builder) instance, player));
+ if (mplc == null) {
+ throw new IllegalArgumentException("Illegal null PlaylistController");
+ }
+ }
+
@Override
public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
return super.setVolumeProvider(volumeProvider);
@@ -1400,6 +1410,16 @@
}
/**
+ * Replace the media item at index in the playlist.
+ * @param index the index of the item to replace
+ * @param item the new item
+ */
+ @Override
+ public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+ mProvider.replacePlaylistItem_impl(index, item);
+ }
+
+ /**
* Return the playlist which is lastly set.
*
* @return playlist
@@ -1461,7 +1481,7 @@
*/
// TODO(jaewan): Unhide or remove
public void registerPlayerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback callback) {
+ @NonNull PlayerEventCallback callback) {
mProvider.registerPlayerEventCallback_impl(executor, callback);
}
@@ -1473,7 +1493,7 @@
* @hide
*/
// TODO(jaewan): Unhide or remove
- public void unregisterPlayerEventCallback(@NonNull EventCallback callback) {
+ public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback callback) {
mProvider.unregisterPlayerEventCallback_impl(callback);
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 209ec42..c0468dc9 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -40,7 +40,7 @@
* <p>
* For ways of retrieving {@link Ringtone} objects or to show a ringtone
* picker, see {@link RingtoneManager}.
- *
+ *
* @see RingtoneManager
*/
public class Ringtone {
@@ -97,7 +97,7 @@
/**
* Sets the stream type where this ringtone will be played.
- *
+ *
* @param streamType The stream, see {@link AudioManager}.
* @deprecated use {@link #setAudioAttributes(AudioAttributes)}
*/
@@ -111,7 +111,7 @@
/**
* Gets the stream type where this ringtone will be played.
- *
+ *
* @return The stream type, see {@link AudioManager}.
* @deprecated use of stream types is deprecated, see
* {@link #setAudioAttributes(AudioAttributes)}
@@ -146,9 +146,8 @@
}
/**
- * @hide
* Sets the player to be looping or non-looping.
- * @param looping whether to loop or not
+ * @param looping whether to loop or not.
*/
public void setLooping(boolean looping) {
synchronized (mPlaybackSettingsLock) {
@@ -158,7 +157,16 @@
}
/**
- * @hide
+ * Returns whether the looping mode was enabled on this player.
+ * @return true if this player loops when playing.
+ */
+ public boolean isLooping() {
+ synchronized (mPlaybackSettingsLock) {
+ return mIsLooping;
+ }
+ }
+
+ /**
* Sets the volume on this player.
* @param volume a raw scalar in range 0.0 to 1.0, where 0.0 mutes this player, and 1.0
* corresponds to no attenuation being applied.
@@ -173,6 +181,16 @@
}
/**
+ * Returns the volume scalar set on this player.
+ * @return a value between 0.0f and 1.0f.
+ */
+ public float getVolume() {
+ synchronized (mPlaybackSettingsLock) {
+ return mVolume;
+ }
+ }
+
+ /**
* Must be called synchronized on mPlaybackSettingsLock
*/
private void applyPlaybackProperties_sync() {
@@ -194,8 +212,8 @@
/**
* Returns a human-presentable title for ringtone. Looks in media
* content provider. If not in either, uses the filename
- *
- * @param context A context used for querying.
+ *
+ * @param context A context used for querying.
*/
public String getTitle(Context context) {
if (mTitle != null) return mTitle;
@@ -265,12 +283,11 @@
if (title == null) {
title = context.getString(com.android.internal.R.string.ringtone_unknown);
-
if (title == null) {
title = "";
}
}
-
+
return title;
}
@@ -395,7 +412,7 @@
/**
* Whether this ringtone is currently playing.
- *
+ *
* @return True if playing, false otherwise.
*/
public boolean isPlaying() {
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 7dbca3b..21d6873 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -39,6 +39,7 @@
* <li> {@link android.media.audiofx.BassBoost}</li>
* <li> {@link android.media.audiofx.PresetReverb}</li>
* <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
+ * <li> {@link android.media.audiofx.DynamicsProcessing}</li>
* </ul>
* <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
* the application must specify the audio session ID of that instance when creating the AudioEffect.
@@ -126,6 +127,12 @@
.fromString("fe3199be-aed0-413f-87bb-11260eb63cf1");
/**
+ * UUID for Dynamics Processing
+ */
+ public static final UUID EFFECT_TYPE_DYNAMICS_PROCESSING = UUID
+ .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
+
+ /**
* Null effect UUID. Used when the UUID for effect type of
* @hide
*/
@@ -203,7 +210,8 @@
* {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
* {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
- * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+ * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
* </li>
* <li>uuid: UUID for this particular implementation</li>
* <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
@@ -224,7 +232,8 @@
* {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
* {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
- * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
* @param uuid UUID for this particular implementation
* @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
* @param name human readable effect name
@@ -246,7 +255,8 @@
* {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
* {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
* {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
- * or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br>
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}
+ * or {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.<br>
* For reverberation, bass boost, EQ and virtualizer, the UUID
* corresponds to the OpenSL ES Interface ID.
*/
@@ -1344,6 +1354,34 @@
/**
* @hide
*/
+ public static float byteArrayToFloat(byte[] valueBuf) {
+ return byteArrayToFloat(valueBuf, 0);
+
+ }
+
+ /**
+ * @hide
+ */
+ public static float byteArrayToFloat(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getFloat(offset);
+
+ }
+
+ /**
+ * @hide
+ */
+ public static byte[] floatToByteArray(float value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putFloat(value);
+ return converter.array();
+ }
+
+ /**
+ * @hide
+ */
public static byte[] concatArrays(byte[]... arrays) {
int len = 0;
for (byte[] a : arrays) {
diff --git a/media/java/android/media/audiofx/DynamicsProcessing.java b/media/java/android/media/audiofx/DynamicsProcessing.java
new file mode 100644
index 0000000..d09c9a8
--- /dev/null
+++ b/media/java/android/media/audiofx/DynamicsProcessing.java
@@ -0,0 +1,2257 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.DynamicsProcessing.Settings;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.StringTokenizer;
+
+/**
+ * DynamicsProcessing is an audio effect for equalizing and changing dynamic range properties of the
+ * sound. It is composed of multiple stages including equalization, multi-band compression and
+ * limiter.
+ * <p>The number of bands and active stages is configurable, and most parameters can be controlled
+ * in realtime, such as gains, attack/release times, thresholds, etc.
+ * <p>The effect is instantiated and controlled by channels. Each channel has the same basic
+ * architecture, but all of their parameters are independent from other channels.
+ * <p>The basic channel configuration is:
+ * <pre>
+ *
+ * Channel 0 Channel 1 .... Channel N-1
+ * Input Input Input
+ * | | |
+ * +----v----+ +----v----+ +----v----+
+ * |inputGain| |inputGain| |inputGain|
+ * +---------+ +---------+ +---------+
+ * | | |
+ * +-----v-----+ +-----v-----+ +-----v-----+
+ * | PreEQ | | PreEQ | | PreEQ |
+ * +-----------+ +-----------+ +-----------+
+ * | | |
+ * +-----v-----+ +-----v-----+ +-----v-----+
+ * | MBC | | MBC | | MBC |
+ * +-----------+ +-----------+ +-----------+
+ * | | |
+ * +-----v-----+ +-----v-----+ +-----v-----+
+ * | PostEQ | | PostEQ | | PostEQ |
+ * +-----------+ +-----------+ +-----------+
+ * | | |
+ * +-----v-----+ +-----v-----+ +-----v-----+
+ * | Limiter | | Limiter | | Limiter |
+ * +-----------+ +-----------+ +-----------+
+ * | | |
+ * Output Output Output
+ * </pre>
+ *
+ * <p>Where the stages are:
+ * inputGain: input gain factor in decibels (dB). 0 dB means no change in level.
+ * PreEQ: Multi-band Equalizer.
+ * MBC: Multi-band Compressor
+ * PostEQ: Multi-band Equalizer
+ * Limiter: Single band compressor/limiter.
+ *
+ * <p>An application creates a DynamicsProcessing object to instantiate and control this audio
+ * effect in the audio framework. A DynamicsProcessor.Config and DynamicsProcessor.Config.Builder
+ * are available to help configure the multiple stages and each band parameters if desired.
+ * <p>See each stage documentation for further details.
+ * <p>If no Config is specified during creation, a default configuration is chosen.
+ * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer,
+ * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect
+ * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}).
+ *
+ * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer, specify the audio
+ * session ID of this AudioTrack or MediaPlayer when constructing the DynamicsProcessing.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
+ */
+
+public final class DynamicsProcessing extends AudioEffect {
+
+ private final static String TAG = "DynamicsProcessing";
+
+ /**
+ * Config object used to initialize and change effect parameters at runtime.
+ */
+ private Config mConfig = null;
+
+
+ // These parameter constants must be synchronized with those in
+ // /system/media/audio_effects/include/audio_effects/effect_dynamicsprocessing.h
+
+ private static final int PARAM_GET_CHANNEL_COUNT = 0x0;
+ private static final int PARAM_EQ_BAND_COUNT = 0x1;
+ private static final int PARAM_MBC_BAND_COUNT = 0x2;
+ private static final int PARAM_INPUT_GAIN = 0x3;
+ private static final int PARAM_PRE_EQ_ENABLED = 0x10;
+ private static final int PARAM_PRE_EQ_BAND_ENABLED = 0x11;
+ private static final int PARAM_PRE_EQ_BAND_FREQUENCY = 0x12;
+ private static final int PARAM_PRE_EQ_BAND_GAIN = 0x13;
+ private static final int PARAM_EQ_FREQUENCY_RANGE = 0x22;
+ private static final int PARAM_EQ_GAIN_RANGE = 0x23;
+ private static final int PARAM_MBC_ENABLED = 0x30;
+ private static final int PARAM_MBC_BAND_ENABLED = 0x31;
+ private static final int PARAM_MBC_BAND_FREQUENCY = 0x32;
+ private static final int PARAM_MBC_BAND_ATTACK_TIME = 0x33;
+ private static final int PARAM_MBC_BAND_RELEASE_TIME = 0x34;
+ private static final int PARAM_MBC_BAND_RATIO = 0x35;
+ private static final int PARAM_MBC_BAND_THRESHOLD = 0x36;
+ private static final int PARAM_MBC_BAND_KNEE_WIDTH = 0x37;
+ private static final int PARAM_MBC_BAND_NOISE_GATE_THRESHOLD = 0x38;
+ private static final int PARAM_MBC_BAND_EXPANDER_RATIO = 0x39;
+ private static final int PARAM_MBC_BAND_GAIN_PRE = 0x3A;
+ private static final int PARAM_MBC_BAND_GAIN_POST = 0x3B;
+ private static final int PARAM_MBC_FREQUENCY_RANGE = 0x42;
+ private static final int PARAM_MBC_ATTACK_TIME_RANGE = 0x43;
+ private static final int PARAM_MBC_RELEASE_TIME_RANGE = 0x44;
+ private static final int PARAM_MBC_RATIO_RANGE = 0x45;
+ private static final int PARAM_MBC_THRESHOLD_RANGE = 0x46;
+ private static final int PARAM_MBC_KNEE_WIDTH_RANGE = 0x47;
+ private static final int PARAM_MBC_NOISE_GATE_THRESHOLD_RANGE = 0x48;
+ private static final int PARAM_MBC_EXPANDER_RATIO_RANGE = 0x49;
+ private static final int PARAM_MBC_GAIN_RANGE = 0x4A;
+ private static final int PARAM_POST_EQ_ENABLED = 0x50;
+ private static final int PARAM_POST_EQ_BAND_ENABLED = 0x51;
+ private static final int PARAM_POST_EQ_BAND_FREQUENCY = 0x52;
+ private static final int PARAM_POST_EQ_BAND_GAIN = 0x53;
+ private static final int PARAM_LIMITER_ENABLED = 0x60;
+ private static final int PARAM_LIMITER_LINK_GROUP = 0x61;
+ private static final int PARAM_LIMITER_ATTACK_TIME = 0x62;
+ private static final int PARAM_LIMITER_RELEASE_TIME = 0x63;
+ private static final int PARAM_LIMITER_RATIO = 0x64;
+ private static final int PARAM_LIMITER_THRESHOLD = 0x65;
+ private static final int PARAM_LIMITER_GAIN_POST = 0x66;
+ private static final int PARAM_LIMITER_ATTACK_TIME_RANGE = 0x72;
+ private static final int PARAM_LIMITER_RELEASE_TIME_RANGE = 0x73;
+ private static final int PARAM_LIMITER_RATIO_RANGE = 0x74;
+ private static final int PARAM_LIMITER_THRESHOLD_RANGE = 0x75;
+ private static final int PARAM_LIMITER_GAIN_RANGE = 0x76;
+ private static final int PARAM_VARIANT = 0x100;
+ private static final int PARAM_VARIANT_DESCRIPTION = 0x101;
+ private static final int PARAM_VARIANT_COUNT = 0x102;
+ private static final int PARAM_SET_ENGINE_ARCHITECTURE = 0x200;
+
+ /**
+ * Index of variant that favors frequency resolution. Frequency domain based implementation.
+ */
+ public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION = 0;
+
+ /**
+ * Index of variant that favors time resolution resolution. Time domain based implementation.
+ */
+ public static final int VARIANT_FAVOR_TIME_RESOLUTION = 1;
+
+ /**
+ * Maximum expected channels to be reported by effect
+ */
+ private static final int CHANNEL_COUNT_MAX = 32;
+
+ /**
+ * Number of channels in effect architecture
+ */
+ private int mChannelCount = 0;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change events
+ * from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+ * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+ */
+ public DynamicsProcessing(int audioSession) {
+ this(0 /*priority*/, audioSession);
+ }
+
+ /**
+ * @hide
+ * Class constructor for the DynamicsProcessing audio effect.
+ * @param priority the priority level requested by the application for controlling the
+ * DynamicsProcessing engine. As the same engine can be shared by several applications,
+ * this parameter indicates how much the requesting application needs control of effect
+ * parameters. The normal priority is 0, above normal is a positive number, below normal a
+ * negative number.
+ * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+ * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+ */
+ public DynamicsProcessing(int priority, int audioSession) {
+ this(priority, audioSession, null);
+ }
+
+ /**
+ * Class constructor for the DynamicsProcessing audio effect
+ * @param priority the priority level requested by the application for controlling the
+ * DynamicsProcessing engine. As the same engine can be shared by several applications,
+ * this parameter indicates how much the requesting application needs control of effect
+ * parameters. The normal priority is 0, above normal is a positive number, below normal a
+ * negative number.
+ * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+ * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+ * @param cfg Config object used to setup the audio effect, including bands per stage, and
+ * specific parameters for each stage/band. Use
+ * {@link android.media.audiofx.DynamicsProcessing.Config.Builder} to create a
+ * Config object that suits your needs. A null cfg parameter will create and use a default
+ * configuration for the effect
+ */
+ public DynamicsProcessing(int priority, int audioSession, Config cfg) {
+ super(EFFECT_TYPE_DYNAMICS_PROCESSING, EFFECT_TYPE_NULL, priority, audioSession);
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching a DynamicsProcessing to global output mix is"
+ + "deprecated!");
+ }
+ mChannelCount = getChannelCount();
+ if (cfg == null) {
+ //create a default configuration and effect, with the number of channels this effect has
+ DynamicsProcessing.Config.Builder builder =
+ new DynamicsProcessing.Config.Builder(
+ CONFIG_DEFAULT_VARIANT,
+ mChannelCount,
+ true /*use preEQ*/, 6 /*pre eq bands*/,
+ true /*use mbc*/, 6 /*mbc bands*/,
+ true /*use postEQ*/, 6 /*postEq bands*/,
+ true /*use Limiter*/);
+ mConfig = builder.build();
+ } else {
+ //validate channels are ok. decide what to do: replicate channels if more, or fail, or
+ mConfig = new DynamicsProcessing.Config(mChannelCount, cfg);
+ }
+
+ setEngineArchitecture(mConfig.getVariant(),
+ mConfig.isPreEqInUse(), mConfig.getPreEqBandCount(),
+ mConfig.isMbcInUse(), mConfig.getMbcBandCount(),
+ mConfig.isPostEqInUse(), mConfig.getPostEqBandCount(),
+ mConfig.isLimiterInUse());
+ }
+
+ /**
+ * Returns the Config object used to setup this effect.
+ * @return Config Current Config object used to setup this DynamicsProcessing effect.
+ */
+ public Config getConfig() {
+ return mConfig;
+ }
+
+
+ private static final int CONFIG_DEFAULT_VARIANT = 0; //favor frequency
+ private static final float CHANNEL_DEFAULT_INPUT_GAIN = 0; // dB
+ private static final float CONFIG_PREFERRED_FRAME_DURATION_MS = 10.0f; //milliseconds
+
+ private static final float EQ_DEFAULT_GAIN = 0; // dB
+ private static final boolean PREEQ_DEFAULT_ENABLED = true;
+ private static final boolean POSTEQ_DEFAULT_ENABLED = true;
+
+
+ private static final boolean MBC_DEFAULT_ENABLED = true;
+ private static final float MBC_DEFAULT_ATTACK_TIME = 50; // ms
+ private static final float MBC_DEFAULT_RELEASE_TIME = 120; // ms
+ private static final float MBC_DEFAULT_RATIO = 2; // 1:N
+ private static final float MBC_DEFAULT_THRESHOLD = -30; // dB
+ private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB
+ private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -80; // dB
+ private static final float MBC_DEFAULT_EXPANDER_RATIO = 1.5f; // N:1
+ private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB
+ private static final float MBC_DEFAULT_POST_GAIN = 10; // dB
+
+ private static final boolean LIMITER_DEFAULT_ENABLED = true;
+ private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//;
+ private static final float LIMITER_DEFAULT_ATTACK_TIME = 50; // ms
+ private static final float LIMITER_DEFAULT_RELEASE_TIME = 120; // ms
+ private static final float LIMITER_DEFAULT_RATIO = 2; // 1:N
+ private static final float LIMITER_DEFAULT_THRESHOLD = -30; // dB
+ private static final float LIMITER_DEFAULT_POST_GAIN = 10; // dB
+
+ private static final float DEFAULT_MIN_FREQUENCY = 220; // Hz
+ private static final float DEFAULT_MAX_FREQUENCY = 20000; // Hz
+ private static final float mMinFreqLog = (float)Math.log10(DEFAULT_MIN_FREQUENCY);
+ private static final float mMaxFreqLog = (float)Math.log10(DEFAULT_MAX_FREQUENCY);
+
+ /**
+ * base class for the different stages.
+ */
+ public static class Stage {
+ private boolean mInUse;
+ private boolean mEnabled;
+ /**
+ * Class constructor for stage
+ * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
+ * set "inUse" at initialization time are not available to be used at any time.
+ * @param enabled true if this stage is currently used to process sound. When disabled,
+ * the stage is bypassed and the sound is copied unaltered from input to output.
+ */
+ public Stage(boolean inUse, boolean enabled) {
+ mInUse = inUse;
+ mEnabled = enabled;
+ }
+
+ /**
+ * returns enabled state of the stage
+ * @return true if stage is enabled for processing, false otherwise
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+ /**
+ * sets enabled state of the stage
+ * @param enabled true for enabled, false otherwise
+ */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ /**
+ * returns inUse state of the stage.
+ * @return inUse state of the stage. True if this stage is currently used to process sound.
+ * When false, the stage is bypassed and the sound is copied unaltered from input to output.
+ */
+ public boolean isInUse() {
+ return mInUse;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format(" Stage InUse: %b\n", isInUse()));
+ if (isInUse()) {
+ sb.append(String.format(" Stage Enabled: %b\n", mEnabled));
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Base class for stages that hold bands
+ */
+ public static class BandStage extends Stage{
+ private int mBandCount;
+ /**
+ * Class constructor for BandStage
+ * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
+ * set "inUse" at initialization time are not available to be used at any time.
+ * @param enabled true if this stage is currently used to process sound. When disabled,
+ * the stage is bypassed and the sound is copied unaltered from input to output.
+ * @param bandCount number of bands this stage will handle. If stage is not inUse, bandcount
+ * is set to 0
+ */
+ public BandStage(boolean inUse, boolean enabled, int bandCount) {
+ super(inUse, enabled);
+ mBandCount = isInUse() ? bandCount : 0;
+ }
+
+ /**
+ * gets number of bands held in this stage
+ * @return number of bands held in this stage
+ */
+ public int getBandCount() {
+ return mBandCount;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ if (isInUse()) {
+ sb.append(String.format(" Band Count: %d\n", mBandCount));
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Base class for bands
+ */
+ public static class BandBase {
+ private boolean mEnabled;
+ private float mCutoffFrequency;
+ /**
+ * Class constructor for BandBase
+ * @param enabled true if this band is currently used to process sound. When false,
+ * the band is effectively muted and sound set to zero.
+ * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+ * effective bandwidth for the band is then computed using this and the previous band
+ * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+ * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+ */
+ public BandBase(boolean enabled, float cutoffFrequency) {
+ mEnabled = enabled;
+ mCutoffFrequency = cutoffFrequency;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format(" Enabled: %b\n", mEnabled));
+ sb.append(String.format(" CutoffFrequency: %f\n", mCutoffFrequency));
+ return sb.toString();
+ }
+
+ /**
+ * returns enabled state of the band
+ * @return true if bands is enabled for processing, false otherwise
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+ /**
+ * sets enabled state of the band
+ * @param enabled true for enabled, false otherwise
+ */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ /**
+ * gets cutoffFrequency for this band in Hertz (Hz)
+ * @return cutoffFrequency for this band in Hertz (Hz)
+ */
+ public float getCutoffFrequency() {
+ return mCutoffFrequency;
+ }
+
+ /**
+ * sets topmost frequency number (in Hz) this band will process. The
+ * effective bandwidth for the band is then computed using this and the previous band
+ * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+ * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+ * @param frequency
+ */
+ public void setCutoffFrequency(float frequency) {
+ mCutoffFrequency = frequency;
+ }
+ }
+
+ /**
+ * Class for Equalizer Bands
+ * Equalizer bands have three controllable parameters: enabled/disabled, cutoffFrequency and
+ * gain
+ */
+ public final static class EqBand extends BandBase {
+ private float mGain;
+ /**
+ * Class constructor for EqBand
+ * @param enabled true if this band is currently used to process sound. When false,
+ * the band is effectively muted and sound set to zero.
+ * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+ * effective bandwidth for the band is then computed using this and the previous band
+ * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+ * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+ * @param gain of equalizer band in decibels (dB). A gain of 0 dB means no change in level.
+ */
+ public EqBand(boolean enabled, float cutoffFrequency, float gain) {
+ super(enabled, cutoffFrequency);
+ mGain = gain;
+ }
+
+ /**
+ * Class constructor for EqBand
+ * @param cfg copy constructor
+ */
+ public EqBand(EqBand cfg) {
+ super(cfg.isEnabled(), cfg.getCutoffFrequency());
+ mGain = cfg.mGain;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ sb.append(String.format(" Gain: %f\n", mGain));
+ return sb.toString();
+ }
+
+ /**
+ * gets current gain of band in decibels (dB)
+ * @return current gain of band in decibels (dB)
+ */
+ public float getGain() {
+ return mGain;
+ }
+
+ /**
+ * sets current gain of band in decibels (dB)
+ * @param gain desired in decibels (db)
+ */
+ public void setGain(float gain) {
+ mGain = gain;
+ }
+ }
+
+ /**
+ * Class for Multi-Band compressor bands
+ * MBC bands have multiple controllable parameters: enabled/disabled, cutoffFrequency,
+ * attackTime, releaseTime, ratio, threshold, kneeWidth, noiseGateThreshold, expanderRatio,
+ * preGain and postGain.
+ */
+ public final static class MbcBand extends BandBase{
+ private float mAttackTime;
+ private float mReleaseTime;
+ private float mRatio;
+ private float mThreshold;
+ private float mKneeWidth;
+ private float mNoiseGateThreshold;
+ private float mExpanderRatio;
+ private float mPreGain;
+ private float mPostGain;
+ /**
+ * Class constructor for MbcBand
+ * @param enabled true if this band is currently used to process sound. When false,
+ * the band is effectively muted and sound set to zero.
+ * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+ * effective bandwidth for the band is then computed using this and the previous band
+ * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+ * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+ * @param attackTime Attack Time for compressor in milliseconds (ms)
+ * @param releaseTime Release Time for compressor in milliseconds (ms)
+ * @param ratio Compressor ratio (1:N)
+ * @param threshold Compressor threshold measured in decibels (dB) from 0 dB Full Scale
+ * (dBFS).
+ * @param kneeWidth Width in decibels (dB) around compressor threshold point.
+ * @param noiseGateThreshold Noise gate threshold in decibels (dB) from 0 dB Full Scale
+ * (dBFS).
+ * @param expanderRatio Expander ratio (N:1) for signals below the Noise Gate Threshold.
+ * @param preGain Gain applied to the signal BEFORE the compression.
+ * @param postGain Gain applied to the signal AFTER compression.
+ */
+ public MbcBand(boolean enabled, float cutoffFrequency, float attackTime, float releaseTime,
+ float ratio, float threshold, float kneeWidth, float noiseGateThreshold,
+ float expanderRatio, float preGain, float postGain) {
+ super(enabled, cutoffFrequency);
+ mAttackTime = attackTime;
+ mReleaseTime = releaseTime;
+ mRatio = ratio;
+ mThreshold = threshold;
+ mKneeWidth = kneeWidth;
+ mNoiseGateThreshold = noiseGateThreshold;
+ mExpanderRatio = expanderRatio;
+ mPreGain = preGain;
+ mPostGain = postGain;
+ }
+
+ /**
+ * Class constructor for MbcBand
+ * @param cfg copy constructor
+ */
+ public MbcBand(MbcBand cfg) {
+ super(cfg.isEnabled(), cfg.getCutoffFrequency());
+ mAttackTime = cfg.mAttackTime;
+ mReleaseTime = cfg.mReleaseTime;
+ mRatio = cfg.mRatio;
+ mThreshold = cfg.mThreshold;
+ mKneeWidth = cfg.mKneeWidth;
+ mNoiseGateThreshold = cfg.mNoiseGateThreshold;
+ mExpanderRatio = cfg.mExpanderRatio;
+ mPreGain = cfg.mPreGain;
+ mPostGain = cfg.mPostGain;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
+ sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
+ sb.append(String.format(" Ratio: 1:%f\n", mRatio));
+ sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
+ sb.append(String.format(" NoiseGateThreshold: %f(dB)\n", mNoiseGateThreshold));
+ sb.append(String.format(" ExpanderRatio: %f:1\n", mExpanderRatio));
+ sb.append(String.format(" PreGain: %f (dB)\n", mPreGain));
+ sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
+ return sb.toString();
+ }
+
+ /**
+ * gets attack time for compressor in milliseconds (ms)
+ * @return attack time for compressor in milliseconds (ms)
+ */
+ public float getAttackTime() { return mAttackTime; }
+ /**
+ * sets attack time for compressor in milliseconds (ms)
+ * @param attackTime desired for compressor in milliseconds (ms)
+ */
+ public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
+ /**
+ * gets release time for compressor in milliseconds (ms)
+ * @return release time for compressor in milliseconds (ms)
+ */
+ public float getReleaseTime() { return mReleaseTime; }
+ /**
+ * sets release time for compressor in milliseconds (ms)
+ * @param releaseTime desired for compressor in milliseconds (ms)
+ */
+ public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
+ /**
+ * gets the compressor ratio (1:N)
+ * @return compressor ratio (1:N)
+ */
+ public float getRatio() { return mRatio; }
+ /**
+ * sets compressor ratio (1:N)
+ * @param ratio desired for the compressor (1:N)
+ */
+ public void setRatio(float ratio) { mRatio = ratio; }
+ /**
+ * gets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+ * Thresholds are negative. A threshold of 0 dB means no compression will take place.
+ * @return compressor threshold in decibels (dB)
+ */
+ public float getThreshold() { return mThreshold; }
+ /**
+ * sets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+ * Thresholds are negative. A threshold of 0 dB means no compression will take place.
+ * @param threshold desired for compressor in decibels(dB)
+ */
+ public void setThreshold(float threshold) { mThreshold = threshold; }
+ /**
+ * get Knee Width in decibels (dB) around compressor threshold point. Widths are always
+ * positive, with higher values representing a wider area of transition from the linear zone
+ * to the compression zone. A knee of 0 dB means a more abrupt transition.
+ * @return Knee Width in decibels (dB)
+ */
+ public float getKneeWidth() { return mKneeWidth; }
+ /**
+ * sets knee width in decibels (dB). See
+ * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getKneeWidth} for more
+ * information.
+ * @param kneeWidth desired in decibels (dB)
+ */
+ public void setKneeWidth(float kneeWidth) { mKneeWidth = kneeWidth; }
+ /**
+ * gets the noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Noise gate
+ * thresholds are negative. Signals below this level will be expanded according the
+ * expanderRatio parameter. A Noise Gate Threshold of -75 dB means very quiet signals might
+ * be effectively removed from the signal.
+ * @return Noise Gate Threshold in decibels (dB)
+ */
+ public float getNoiseGateThreshold() { return mNoiseGateThreshold; }
+ /**
+ * sets noise gate threshod in decibels (dB). See
+ * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getNoiseGateThreshold} for more
+ * information.
+ * @param noiseGateThreshold desired in decibels (dB)
+ */
+ public void setNoiseGateThreshold(float noiseGateThreshold) {
+ mNoiseGateThreshold = noiseGateThreshold; }
+ /**
+ * gets Expander ratio (N:1) for signals below the Noise Gate Threshold.
+ * @return Expander ratio (N:1)
+ */
+ public float getExpanderRatio() { return mExpanderRatio; }
+ /**
+ * sets Expander ratio (N:1) for signals below the Noise Gate Threshold.
+ * @param expanderRatio desired expander ratio (N:1)
+ */
+ public void setExpanderRatio(float expanderRatio) { mExpanderRatio = expanderRatio; }
+ /**
+ * gets the gain applied to the signal BEFORE the compression. Measured in decibels (dB)
+ * where 0 dB means no level change.
+ * @return preGain value in decibels (dB)
+ */
+ public float getPreGain() { return mPreGain; }
+ /**
+ * sets the gain to be applied to the signal BEFORE the compression, measured in decibels
+ * (dB), where 0 dB means no level change.
+ * @param preGain desired in decibels (dB)
+ */
+ public void setPreGain(float preGain) { mPreGain = preGain; }
+ /**
+ * gets the gain applied to the signal AFTER compression. Measured in decibels (dB) where 0
+ * dB means no level change
+ * @return postGain value in decibels (dB)
+ */
+ public float getPostGain() { return mPostGain; }
+ /**
+ * sets the gain to be applied to the siganl AFTER the compression. Measured in decibels
+ * (dB), where 0 dB means no level change.
+ * @param postGain desired value in decibels (dB)
+ */
+ public void setPostGain(float postGain) { mPostGain = postGain; }
+ }
+
+ /**
+ * Class for Equalizer stage
+ */
+ public final static class Eq extends BandStage {
+ private final EqBand[] mBands;
+ /**
+ * Class constructor for Equalizer (Eq) stage
+ * @param inUse true if Eq stage will be used, false otherwise.
+ * @param enabled true if Eq stage is enabled/disabled. This can be changed while effect is
+ * running
+ * @param bandCount number of bands for this Equalizer stage. Can't be changed while effect
+ * is running
+ */
+ public Eq(boolean inUse, boolean enabled, int bandCount) {
+ super(inUse, enabled, bandCount);
+ if (isInUse()) {
+ mBands = new EqBand[bandCount];
+ for (int b = 0; b < bandCount; b++) {
+ float freq = DEFAULT_MAX_FREQUENCY;
+ if (bandCount > 1) {
+ freq = (float)Math.pow(10, mMinFreqLog +
+ b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
+ }
+ mBands[b] = new EqBand(true, freq, EQ_DEFAULT_GAIN);
+ }
+ } else {
+ mBands = null;
+ }
+ }
+ /**
+ * Class constructor for Eq stage
+ * @param cfg copy constructor
+ */
+ public Eq(Eq cfg) {
+ super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
+ if (isInUse()) {
+ mBands = new EqBand[cfg.mBands.length];
+ for (int b = 0; b < mBands.length; b++) {
+ mBands[b] = new EqBand(cfg.mBands[b]);
+ }
+ } else {
+ mBands = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ if (isInUse()) {
+ sb.append("--->EqBands: " + mBands.length + "\n");
+ for (int b = 0; b < mBands.length; b++) {
+ sb.append(String.format(" Band %d\n", b));
+ sb.append(mBands[b].toString());
+ }
+ }
+ return sb.toString();
+ }
+ /**
+ * Helper function to check if band index is within range
+ * @param band index to check
+ */
+ private void checkBand(int band) {
+ if (mBands == null || band < 0 || band >= mBands.length) {
+ throw new IllegalArgumentException("band index " + band +" out of bounds");
+ }
+ }
+ /**
+ * Sets EqBand object for given band index
+ * @param band index of band to be modified
+ * @param bandCfg EqBand object.
+ */
+ public void setBand(int band, EqBand bandCfg) {
+ checkBand(band);
+ mBands[band] = new EqBand(bandCfg);
+ }
+ /**
+ * Gets EqBand object for band of interest.
+ * @param band index of band of interest
+ * @return EqBand Object
+ */
+ public EqBand getBand(int band) {
+ checkBand(band);
+ return mBands[band];
+ }
+ }
+
+ /**
+ * Class for Multi-Band Compressor (MBC) stage
+ */
+ public final static class Mbc extends BandStage {
+ private final MbcBand[] mBands;
+ /**
+ * Constructor for Multi-Band Compressor (MBC) stage
+ * @param inUse true if MBC stage will be used, false otherwise.
+ * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
+ * is running
+ * @param bandCount number of bands for this MBC stage. Can't be changed while effect is
+ * running
+ */
+ public Mbc(boolean inUse, boolean enabled, int bandCount) {
+ super(inUse, enabled, bandCount);
+ if (isInUse()) {
+ mBands = new MbcBand[bandCount];
+ for (int b = 0; b < bandCount; b++) {
+ float freq = DEFAULT_MAX_FREQUENCY;
+ if (bandCount > 1) {
+ freq = (float)Math.pow(10, mMinFreqLog +
+ b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
+ }
+ mBands[b] = new MbcBand(true, freq, MBC_DEFAULT_ATTACK_TIME,
+ MBC_DEFAULT_RELEASE_TIME, MBC_DEFAULT_RATIO,
+ MBC_DEFAULT_THRESHOLD, MBC_DEFAULT_KNEE_WIDTH,
+ MBC_DEFAULT_NOISE_GATE_THRESHOLD, MBC_DEFAULT_EXPANDER_RATIO,
+ MBC_DEFAULT_PRE_GAIN, MBC_DEFAULT_POST_GAIN);
+ }
+ } else {
+ mBands = null;
+ }
+ }
+ /**
+ * Class constructor for MBC stage
+ * @param cfg copy constructor
+ */
+ public Mbc(Mbc cfg) {
+ super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
+ if (isInUse()) {
+ mBands = new MbcBand[cfg.mBands.length];
+ for (int b = 0; b < mBands.length; b++) {
+ mBands[b] = new MbcBand(cfg.mBands[b]);
+ }
+ } else {
+ mBands = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ if (isInUse()) {
+ sb.append("--->MbcBands: " + mBands.length + "\n");
+ for (int b = 0; b < mBands.length; b++) {
+ sb.append(String.format(" Band %d\n", b));
+ sb.append(mBands[b].toString());
+ }
+ }
+ return sb.toString();
+ }
+ /**
+ * Helper function to check if band index is within range
+ * @param band index to check
+ */
+ private void checkBand(int band) {
+ if (mBands == null || band < 0 || band >= mBands.length) {
+ throw new IllegalArgumentException("band index " + band +" out of bounds");
+ }
+ }
+ /**
+ * Sets MbcBand object for given band index
+ * @param band index of band to be modified
+ * @param bandCfg MbcBand object.
+ */
+ public void setBand(int band, MbcBand bandCfg) {
+ checkBand(band);
+ mBands[band] = new MbcBand(bandCfg);
+ }
+ /**
+ * Gets MbcBand object for band of interest.
+ * @param band index of band of interest
+ * @return MbcBand Object
+ */
+ public MbcBand getBand(int band) {
+ checkBand(band);
+ return mBands[band];
+ }
+ }
+
+ /**
+ * Class for Limiter Stage
+ * Limiter is a single band compressor at the end of the processing chain, commonly used to
+ * protect the signal from overloading and distortion. Limiters have multiple controllable
+ * parameters: enabled/disabled, linkGroup, attackTime, releaseTime, ratio, threshold, and
+ * postGain.
+ * <p>Limiters can be linked in groups across multiple channels. Linked limiters will trigger
+ * the same limiting if any of the linked limiters starts compressing.
+ */
+ public final static class Limiter extends Stage {
+ private int mLinkGroup;
+ private float mAttackTime;
+ private float mReleaseTime;
+ private float mRatio;
+ private float mThreshold;
+ private float mPostGain;
+
+ /**
+ * Class constructor for Limiter Stage
+ * @param inUse true if MBC stage will be used, false otherwise.
+ * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
+ * is running
+ * @param linkGroup index of group assigned to this Limiter. Only limiters that share the
+ * same linkGroup index will react together.
+ * @param attackTime Attack Time for limiter compressor in milliseconds (ms)
+ * @param releaseTime Release Time for limiter compressor in milliseconds (ms)
+ * @param ratio Limiter Compressor ratio (1:N)
+ * @param threshold Limiter Compressor threshold measured in decibels (dB) from 0 dB Full
+ * Scale (dBFS).
+ * @param postGain Gain applied to the signal AFTER compression.
+ */
+ public Limiter(boolean inUse, boolean enabled, int linkGroup, float attackTime,
+ float releaseTime, float ratio, float threshold, float postGain) {
+ super(inUse, enabled);
+ mLinkGroup = linkGroup;
+ mAttackTime = attackTime;
+ mReleaseTime = releaseTime;
+ mRatio = ratio;
+ mThreshold = threshold;
+ mPostGain = postGain;
+ }
+
+ /**
+ * Class Constructor for Limiter
+ * @param cfg copy constructor
+ */
+ public Limiter(Limiter cfg) {
+ super(cfg.isInUse(), cfg.isEnabled());
+ mLinkGroup = cfg.mLinkGroup;
+ mAttackTime = cfg.mAttackTime;
+ mReleaseTime = cfg.mReleaseTime;
+ mRatio = cfg.mRatio;
+ mThreshold = cfg.mThreshold;
+ mPostGain = cfg.mPostGain;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ if (isInUse()) {
+ sb.append(String.format(" LinkGroup: %d (group)\n", mLinkGroup));
+ sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
+ sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
+ sb.append(String.format(" Ratio: 1:%f\n", mRatio));
+ sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
+ sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
+ }
+ return sb.toString();
+ }
+ /**
+ * Gets the linkGroup index for this Limiter Stage. Only limiters that share the same
+ * linkGroup index will react together.
+ * @return linkGroup index.
+ */
+ public int getLinkGroup() { return mLinkGroup; }
+ /**
+ * Sets the linkGroup index for this limiter Stage.
+ * @param linkGroup desired linkGroup index
+ */
+ public void setLinkGroup(int linkGroup) { mLinkGroup = linkGroup; }
+ /**
+ * gets attack time for limiter compressor in milliseconds (ms)
+ * @return attack time for limiter compressor in milliseconds (ms)
+ */
+ public float getAttackTime() { return mAttackTime; }
+ /**
+ * sets attack time for limiter compressor in milliseconds (ms)
+ * @param attackTime desired for limiter compressor in milliseconds (ms)
+ */
+ public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
+ /**
+ * gets release time for limiter compressor in milliseconds (ms)
+ * @return release time for limiter compressor in milliseconds (ms)
+ */
+ public float getReleaseTime() { return mReleaseTime; }
+ /**
+ * sets release time for limiter compressor in milliseconds (ms)
+ * @param releaseTime desired for limiter compressor in milliseconds (ms)
+ */
+ public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
+ /**
+ * gets the limiter compressor ratio (1:N)
+ * @return limiter compressor ratio (1:N)
+ */
+ public float getRatio() { return mRatio; }
+ /**
+ * sets limiter compressor ratio (1:N)
+ * @param ratio desired for the limiter compressor (1:N)
+ */
+ public void setRatio(float ratio) { mRatio = ratio; }
+ /**
+ * gets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
+ * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
+ * @return limiter compressor threshold in decibels (dB)
+ */
+ public float getThreshold() { return mThreshold; }
+ /**
+ * sets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
+ * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
+ * @param threshold desired for limiter compressor in decibels(dB)
+ */
+ public void setThreshold(float threshold) { mThreshold = threshold; }
+ /**
+ * gets the gain applied to the signal AFTER limiting. Measured in decibels (dB) where 0
+ * dB means no level change
+ * @return postGain value in decibels (dB)
+ */
+ public float getPostGain() { return mPostGain; }
+ /**
+ * sets the gain to be applied to the siganl AFTER the limiter. Measured in decibels
+ * (dB), where 0 dB means no level change.
+ * @param postGain desired value in decibels (dB)
+ */
+ public void setPostGain(float postGain) { mPostGain = postGain; }
+ }
+
+ /**
+ * Class for Channel configuration parameters. It is composed of multiple stages, which can be
+ * used/enabled independently. Stages not used or disabled will be bypassed and the sound would
+ * be unaffected by them.
+ */
+ public final static class Channel {
+ private float mInputGain;
+ private Eq mPreEq;
+ private Mbc mMbc;
+ private Eq mPostEq;
+ private Limiter mLimiter;
+
+ /**
+ * Class constructor for Channel configuration.
+ * @param inputGain value in decibels (dB) of level change applied to the audio before
+ * processing. A value of 0 dB means no change.
+ * @param preEqInUse true if PreEq stage will be used, false otherwise. This can't be
+ * changed later.
+ * @param preEqBandCount number of bands for PreEq stage. This can't be changed later.
+ * @param mbcInUse true if Mbc stage will be used, false otherwise. This can't be changed
+ * later.
+ * @param mbcBandCount number of bands for Mbc stage. This can't be changed later.
+ * @param postEqInUse true if PostEq stage will be used, false otherwise. This can't be
+ * changed later.
+ * @param postEqBandCount number of bands for PostEq stage. This can't be changed later.
+ * @param limiterInUse true if Limiter stage will be used, false otherwise. This can't be
+ * changed later.
+ */
+ public Channel (float inputGain,
+ boolean preEqInUse, int preEqBandCount,
+ boolean mbcInUse, int mbcBandCount,
+ boolean postEqInUse, int postEqBandCount,
+ boolean limiterInUse) {
+ mInputGain = inputGain;
+ mPreEq = new Eq(preEqInUse, PREEQ_DEFAULT_ENABLED, preEqBandCount);
+ mMbc = new Mbc(mbcInUse, MBC_DEFAULT_ENABLED, mbcBandCount);
+ mPostEq = new Eq(postEqInUse, POSTEQ_DEFAULT_ENABLED,
+ postEqBandCount);
+ mLimiter = new Limiter(limiterInUse,
+ LIMITER_DEFAULT_ENABLED, LIMITER_DEFAULT_LINK_GROUP,
+ LIMITER_DEFAULT_ATTACK_TIME, LIMITER_DEFAULT_RELEASE_TIME,
+ LIMITER_DEFAULT_RATIO, LIMITER_DEFAULT_THRESHOLD, LIMITER_DEFAULT_POST_GAIN);
+ }
+
+ /**
+ * Class constructor for Channel configuration
+ * @param cfg copy constructor
+ */
+ public Channel(Channel cfg) {
+ mInputGain = cfg.mInputGain;
+ mPreEq = new Eq(cfg.mPreEq);
+ mMbc = new Mbc(cfg.mMbc);
+ mPostEq = new Eq(cfg.mPostEq);
+ mLimiter = new Limiter(cfg.mLimiter);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format(" InputGain: %f\n", mInputGain));
+ sb.append("-->PreEq\n");
+ sb.append(mPreEq.toString());
+ sb.append("-->MBC\n");
+ sb.append(mMbc.toString());
+ sb.append("-->PostEq\n");
+ sb.append(mPostEq.toString());
+ sb.append("-->Limiter\n");
+ sb.append(mLimiter.toString());
+ return sb.toString();
+ }
+ /**
+ * Gets inputGain value in decibels (dB). 0 dB means no change;
+ * @return gain value in decibels (dB)
+ */
+ public float getInputGain() {
+ return mInputGain;
+ }
+ /**
+ * Sets inputGain value in decibels (dB). 0 dB means no change;
+ * @param inputGain desired gain value in decibels (dB)
+ */
+ public void setInputGain(float inputGain) {
+ mInputGain = inputGain;
+ }
+
+ /**
+ * Gets PreEq configuration stage
+ * @return PreEq configuration stage
+ */
+ public Eq getPreEq() {
+ return mPreEq;
+ }
+ /**
+ * Sets PreEq configuration stage. New PreEq stage must have the same number of bands than
+ * original PreEq stage.
+ * @param preEq configuration
+ */
+ public void setPreEq(Eq preEq) {
+ if (preEq.getBandCount() != mPreEq.getBandCount()) {
+ throw new IllegalArgumentException("PreEqBandCount changed from " +
+ mPreEq.getBandCount() + " to " + preEq.getBandCount());
+ }
+ mPreEq = new Eq(preEq);
+ }
+ /**
+ * Gets EqBand for PreEq stage for given band index.
+ * @param band index of band of interest from PreEq stage
+ * @return EqBand configuration
+ */
+ public EqBand getPreEqBand(int band) {
+ return mPreEq.getBand(band);
+ }
+ /**
+ * Sets EqBand for PreEq stage for given band index
+ * @param band index of band of interest from PreEq stage
+ * @param preEqBand configuration to be set.
+ */
+ public void setPreEqBand(int band, EqBand preEqBand) {
+ mPreEq.setBand(band, preEqBand);
+ }
+
+ /**
+ * Gets Mbc configuration stage
+ * @return Mbc configuration stage
+ */
+ public Mbc getMbc() {
+ return mMbc;
+ }
+ /**
+ * Sets Mbc configuration stage. New Mbc stage must have the same number of bands than
+ * original Mbc stage.
+ * @param mbc
+ */
+ public void setMbc(Mbc mbc) {
+ if (mbc.getBandCount() != mMbc.getBandCount()) {
+ throw new IllegalArgumentException("MbcBandCount changed from " +
+ mMbc.getBandCount() + " to " + mbc.getBandCount());
+ }
+ mMbc = new Mbc(mbc);
+ }
+ /**
+ * Gets MbcBand configuration for Mbc stage, for given band index.
+ * @param band index of band of interest from Mbc stage
+ * @return MbcBand configuration
+ */
+ public MbcBand getMbcBand(int band) {
+ return mMbc.getBand(band);
+ }
+ /**
+ * Sets MbcBand for Mbc stage for given band index
+ * @param band index of band of interest from Mbc Stage
+ * @param mbcBand configuration to be set
+ */
+ public void setMbcBand(int band, MbcBand mbcBand) {
+ mMbc.setBand(band, mbcBand);
+ }
+
+ /**
+ * Gets PostEq configuration stage
+ * @return PostEq configuration stage
+ */
+ public Eq getPostEq() {
+ return mPostEq;
+ }
+ /**
+ * Sets PostEq configuration stage. New PostEq stage must have the same number of bands than
+ * original PostEq stage.
+ * @param postEq configuration
+ */
+ public void setPostEq(Eq postEq) {
+ if (postEq.getBandCount() != mPostEq.getBandCount()) {
+ throw new IllegalArgumentException("PostEqBandCount changed from " +
+ mPostEq.getBandCount() + " to " + postEq.getBandCount());
+ }
+ mPostEq = new Eq(postEq);
+ }
+ /**
+ * Gets EqBand for PostEq stage for given band index.
+ * @param band index of band of interest from PostEq stage
+ * @return EqBand configuration
+ */
+ public EqBand getPostEqBand(int band) {
+ return mPostEq.getBand(band);
+ }
+ /**
+ * Sets EqBand for PostEq stage for given band index
+ * @param band index of band of interest from PostEq stage
+ * @param postEqBand configuration to be set.
+ */
+ public void setPostEqBand(int band, EqBand postEqBand) {
+ mPostEq.setBand(band, postEqBand);
+ }
+
+ /**
+ * Gets Limiter configuration stage
+ * @return Limiter configuration stage
+ */
+ public Limiter getLimiter() {
+ return mLimiter;
+ }
+ /**
+ * Sets Limiter configuration stage.
+ * @param limiter configuration stage.
+ */
+ public void setLimiter(Limiter limiter) {
+ mLimiter = new Limiter(limiter);
+ }
+ }
+
+ /**
+ * Class for Config object, used by DynamicsProcessing to configure and update the audio effect.
+ * use Builder to instantiate objects of this type.
+ */
+ public final static class Config {
+ private final int mVariant;
+ private final int mChannelCount;
+ private final boolean mPreEqInUse;
+ private final int mPreEqBandCount;
+ private final boolean mMbcInUse;
+ private final int mMbcBandCount;
+ private final boolean mPostEqInUse;
+ private final int mPostEqBandCount;
+ private final boolean mLimiterInUse;
+ private final float mPreferredFrameDuration;
+ private final Channel[] mChannel;
+
+ /**
+ * @hide
+ * Class constructor for config. None of these parameters can be changed later.
+ * @param variant index of variant used for effect engine. See
+ * {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
+ * @param frameDurationMs preferred frame duration in milliseconds (ms).
+ * @param channelCount Number of channels to be configured.
+ * @param preEqInUse true if PreEq stage will be used, false otherwise.
+ * @param preEqBandCount number of bands for PreEq stage.
+ * @param mbcInUse true if Mbc stage will be used, false otherwise.
+ * @param mbcBandCount number of bands for Mbc stage.
+ * @param postEqInUse true if PostEq stage will be used, false otherwise.
+ * @param postEqBandCount number of bands for PostEq stage.
+ * @param limiterInUse true if Limiter stage will be used, false otherwise.
+ * @param channel array of Channel objects to be used for this configuration.
+ */
+ public Config(int variant, float frameDurationMs, int channelCount,
+ boolean preEqInUse, int preEqBandCount,
+ boolean mbcInUse, int mbcBandCount,
+ boolean postEqInUse, int postEqBandCount,
+ boolean limiterInUse,
+ Channel[] channel) {
+ mVariant = variant;
+ mPreferredFrameDuration = frameDurationMs;
+ mChannelCount = channelCount;
+ mPreEqInUse = preEqInUse;
+ mPreEqBandCount = preEqBandCount;
+ mMbcInUse = mbcInUse;
+ mMbcBandCount = mbcBandCount;
+ mPostEqInUse = postEqInUse;
+ mPostEqBandCount = postEqBandCount;
+ mLimiterInUse = limiterInUse;
+
+ mChannel = new Channel[mChannelCount];
+ //check if channelconfig is null or has less channels than channel count.
+ //options: fill the missing with default options.
+ // or fail?
+ for (int ch = 0; ch < mChannelCount; ch++) {
+ if (ch < channel.length) {
+ mChannel[ch] = new Channel(channel[ch]); //copy create
+ } else {
+ //create a new one from scratch? //fail?
+ }
+ }
+ }
+ //a version that will scale to necessary number of channels
+ /**
+ * @hide
+ * Class constructor for Configuration.
+ * @param channelCount limit configuration to this number of channels. if channelCount is
+ * greater than number of channels in cfg, the constructor will duplicate the last channel
+ * found as many times as necessary to create a Config with channelCount number of channels.
+ * If channelCount is less than channels in cfg, the extra channels in cfg will be ignored.
+ * @param cfg copy constructor paremter.
+ */
+ public Config(int channelCount, Config cfg) {
+ mVariant = cfg.mVariant;
+ mPreferredFrameDuration = cfg.mPreferredFrameDuration;
+ mChannelCount = cfg.mChannelCount;
+ mPreEqInUse = cfg.mPreEqInUse;
+ mPreEqBandCount = cfg.mPreEqBandCount;
+ mMbcInUse = cfg.mMbcInUse;
+ mMbcBandCount = cfg.mMbcBandCount;
+ mPostEqInUse = cfg.mPostEqInUse;
+ mPostEqBandCount = cfg.mPostEqBandCount;
+ mLimiterInUse = cfg.mLimiterInUse;
+
+ if (mChannelCount != cfg.mChannel.length) {
+ throw new IllegalArgumentException("configuration channel counts differ " +
+ mChannelCount + " !=" + cfg.mChannel.length);
+ }
+ if (channelCount < 1) {
+ throw new IllegalArgumentException("channel resizing less than 1 not allowed");
+ }
+
+ mChannel = new Channel[channelCount];
+ for (int ch = 0; ch < channelCount; ch++) {
+ if (ch < mChannelCount) {
+ mChannel[ch] = new Channel(cfg.mChannel[ch]);
+ } else {
+ //duplicate last
+ mChannel[ch] = new Channel(cfg.mChannel[mChannelCount-1]);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Class constructor for Config
+ * @param cfg Configuration object copy constructor
+ */
+ public Config(Config cfg) {
+ this(cfg.mChannelCount, cfg);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Variant: %d\n", mVariant));
+ sb.append(String.format("PreferredFrameDuration: %f\n", mPreferredFrameDuration));
+ sb.append(String.format("ChannelCount: %d\n", mChannelCount));
+ sb.append(String.format("PreEq inUse: %b, bandCount:%d\n",mPreEqInUse,
+ mPreEqBandCount));
+ sb.append(String.format("Mbc inUse: %b, bandCount: %d\n",mMbcInUse, mMbcBandCount));
+ sb.append(String.format("PostEq inUse: %b, bandCount: %d\n", mPostEqInUse,
+ mPostEqBandCount));
+ sb.append(String.format("Limiter inUse: %b\n", mLimiterInUse));
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ sb.append(String.format("==Channel %d\n", ch));
+ sb.append(mChannel[ch].toString());
+ }
+ return sb.toString();
+ }
+ private void checkChannel(int channelIndex) {
+ if (channelIndex < 0 || channelIndex >= mChannel.length) {
+ throw new IllegalArgumentException("ChannelIndex out of bounds");
+ }
+ }
+
+ //getters and setters
+ /**
+ * Gets variant for effect engine See {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and
+ * {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
+ * @return variant of effect engine
+ */
+ public int getVariant() {
+ return mVariant;
+ }
+ /**
+ * Gets preferred frame duration in milliseconds (ms).
+ * @return preferred frame duration in milliseconds (ms)
+ */
+ public float getPreferredFrameDuration() {
+ return mPreferredFrameDuration;
+ }
+ /**
+ * Gets if preEq stage is in use
+ * @return true if preEq stage is in use;
+ */
+ public boolean isPreEqInUse() {
+ return mPreEqInUse;
+ }
+ /**
+ * Gets number of bands configured for the PreEq stage.
+ * @return number of bands configured for the PreEq stage.
+ */
+ public int getPreEqBandCount() {
+ return mPreEqBandCount;
+ }
+ /**
+ * Gets if Mbc stage is in use
+ * @return true if Mbc stage is in use;
+ */
+ public boolean isMbcInUse() {
+ return mMbcInUse;
+ }
+ /**
+ * Gets number of bands configured for the Mbc stage.
+ * @return number of bands configured for the Mbc stage.
+ */
+ public int getMbcBandCount() {
+ return mMbcBandCount;
+ }
+ /**
+ * Gets if PostEq stage is in use
+ * @return true if PostEq stage is in use;
+ */
+ public boolean isPostEqInUse() {
+ return mPostEqInUse;
+ }
+ /**
+ * Gets number of bands configured for the PostEq stage.
+ * @return number of bands configured for the PostEq stage.
+ */
+ public int getPostEqBandCount() {
+ return mPostEqBandCount;
+ }
+ /**
+ * Gets if Limiter stage is in use
+ * @return true if Limiter stage is in use;
+ */
+ public boolean isLimiterInUse() {
+ return mLimiterInUse;
+ }
+
+ //channel
+ /**
+ * Gets the Channel configuration object by using the channel index
+ * @param channelIndex of desired Channel object
+ * @return Channel configuration object
+ */
+ public Channel getChannelByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex];
+ }
+
+ /**
+ * Sets the chosen Channel object in the selected channelIndex
+ * Note that all the stages should have the same number of bands than the existing Channel
+ * object.
+ * @param channelIndex index of channel to be replaced
+ * @param channel Channel configuration object to be set
+ */
+ public void setChannelTo(int channelIndex, Channel channel) {
+ checkChannel(channelIndex);
+ //check all things are compatible
+ if (mMbcBandCount != channel.getMbc().getBandCount()) {
+ throw new IllegalArgumentException("MbcBandCount changed from " +
+ mMbcBandCount + " to " + channel.getPreEq().getBandCount());
+ }
+ if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
+ throw new IllegalArgumentException("PreEqBandCount changed from " +
+ mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
+ }
+ if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
+ throw new IllegalArgumentException("PostEqBandCount changed from " +
+ mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
+ }
+ mChannel[channelIndex] = new Channel(channel);
+ }
+
+ /**
+ * Sets ALL channels to the chosen Channel object. Note that all the stages should have the
+ * same number of bands than the existing ones.
+ * @param channel Channel configuration object to be set.
+ */
+ public void setAllChannelsTo(Channel channel) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setChannelTo(ch, channel);
+ }
+ }
+
+ //===channel params
+ /**
+ * Gets inputGain value in decibels (dB) for channel indicated by channelIndex
+ * @param channelIndex index of channel of interest
+ * @return inputGain value in decibels (dB). 0 dB means no change.
+ */
+ public float getInputGainByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getInputGain();
+ }
+ /**
+ * Sets the inputGain value in decibels (dB) for the channel indicated by channelIndex.
+ * @param channelIndex index of channel of interest
+ * @param inputGain desired value in decibels (dB).
+ */
+ public void setInputGainByChannelIndex(int channelIndex, float inputGain) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setInputGain(inputGain);
+ }
+ /**
+ * Sets the inputGain value in decibels (dB) for ALL channels
+ * @param inputGain desired value in decibels (dB)
+ */
+ public void setInputGainAllChannelsTo(float inputGain) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setInputGain(inputGain);
+ }
+ }
+
+ //=== PreEQ
+ /**
+ * Gets PreEq stage from channel indicated by channelIndex
+ * @param channelIndex index of channel of interest
+ * @return PreEq stage configuration object
+ */
+ public Eq getPreEqByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getPreEq();
+ }
+ /**
+ * Sets the PreEq stage configuration for the channel indicated by channelIndex. Note that
+ * new preEq stage must have the same number of bands than original preEq stage
+ * @param channelIndex index of channel to be set
+ * @param preEq desired PreEq configuration to be set
+ */
+ public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPreEq(preEq);
+ }
+ /**
+ * Sets the PreEq stage configuration for ALL channels. Note that new preEq stage must have
+ * the same number of bands than original preEq stages.
+ * @param preEq desired PreEq configuration to be set
+ */
+ public void setPreEqAllChannelsTo(Eq preEq) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setPreEq(preEq);
+ }
+ }
+ public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getPreEqBand(band);
+ }
+ public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPreEqBand(band, preEqBand);
+ }
+ public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setPreEqBand(band, preEqBand);
+ }
+ }
+
+ //=== MBC
+ public Mbc getMbcByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getMbc();
+ }
+ public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setMbc(mbc);
+ }
+ public void setMbcAllChannelsTo(Mbc mbc) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setMbc(mbc);
+ }
+ }
+ public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getMbcBand(band);
+ }
+ public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setMbcBand(band, mbcBand);
+ }
+ public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setMbcBand(band, mbcBand);
+ }
+ }
+
+ //=== PostEQ
+ public Eq getPostEqByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getPostEq();
+ }
+ public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPostEq(postEq);
+ }
+ public void setPostEqAllChannelsTo(Eq postEq) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setPostEq(postEq);
+ }
+ }
+ public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getPostEqBand(band);
+ }
+ public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPostEqBand(band, postEqBand);
+ }
+ public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setPostEqBand(band, postEqBand);
+ }
+ }
+
+ //Limiter
+ public Limiter getLimiterByChannelIndex(int channelIndex) {
+ checkChannel(channelIndex);
+ return mChannel[channelIndex].getLimiter();
+ }
+ public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setLimiter(limiter);
+ }
+ public void setLimiterAllChannelsTo(Limiter limiter) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setLimiter(limiter);
+ }
+ }
+
+ public final static class Builder {
+ private int mVariant;
+ private int mChannelCount;
+ private boolean mPreEqInUse;
+ private int mPreEqBandCount;
+ private boolean mMbcInUse;
+ private int mMbcBandCount;
+ private boolean mPostEqInUse;
+ private int mPostEqBandCount;
+ private boolean mLimiterInUse;
+ private float mPreferredFrameDuration = CONFIG_PREFERRED_FRAME_DURATION_MS;
+ private Channel[] mChannel;
+
+ public Builder(int variant, int channelCount,
+ boolean preEqInUse, int preEqBandCount,
+ boolean mbcInUse, int mbcBandCount,
+ boolean postEqInUse, int postEqBandCount,
+ boolean limiterInUse) {
+ mVariant = variant;
+ mChannelCount = channelCount;
+ mPreEqInUse = preEqInUse;
+ mPreEqBandCount = preEqBandCount;
+ mMbcInUse = mbcInUse;
+ mMbcBandCount = mbcBandCount;
+ mPostEqInUse = postEqInUse;
+ mPostEqBandCount = postEqBandCount;
+ mLimiterInUse = limiterInUse;
+ mChannel = new Channel[mChannelCount];
+ for (int ch = 0; ch < mChannelCount; ch++) {
+ this.mChannel[ch] = new Channel(CHANNEL_DEFAULT_INPUT_GAIN,
+ this.mPreEqInUse, this.mPreEqBandCount,
+ this.mMbcInUse, this.mMbcBandCount,
+ this.mPostEqInUse, this.mPostEqBandCount,
+ this.mLimiterInUse);
+ }
+ }
+
+ private void checkChannel(int channelIndex) {
+ if (channelIndex < 0 || channelIndex >= mChannel.length) {
+ throw new IllegalArgumentException("ChannelIndex out of bounds");
+ }
+ }
+
+ public Builder setPreferredFrameDuration(float frameDuration) {
+ if (frameDuration < 0) {
+ throw new IllegalArgumentException("Expected positive frameDuration");
+ }
+ mPreferredFrameDuration = frameDuration;
+ return this;
+ }
+
+ public Builder setInputGainByChannelIndex(int channelIndex, float inputGain) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setInputGain(inputGain);
+ return this;
+ }
+ public Builder setInputGainAllChannelsTo(float inputGain) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ mChannel[ch].setInputGain(inputGain);
+ }
+ return this;
+ }
+
+ public Builder setChannelTo(int channelIndex, Channel channel) {
+ checkChannel(channelIndex);
+ //check all things are compatible
+ if (mMbcBandCount != channel.getMbc().getBandCount()) {
+ throw new IllegalArgumentException("MbcBandCount changed from " +
+ mMbcBandCount + " to " + channel.getPreEq().getBandCount());
+ }
+ if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
+ throw new IllegalArgumentException("PreEqBandCount changed from " +
+ mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
+ }
+ if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
+ throw new IllegalArgumentException("PostEqBandCount changed from " +
+ mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
+ }
+ mChannel[channelIndex] = new Channel(channel);
+ return this;
+ }
+ public Builder setAllChannelsTo(Channel channel) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setChannelTo(ch, channel);
+ }
+ return this;
+ }
+
+ public Builder setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPreEq(preEq);
+ return this;
+ }
+ public Builder setPreEqAllChannelsTo(Eq preEq) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setPreEqByChannelIndex(ch, preEq);
+ }
+ return this;
+ }
+
+ public Builder setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setMbc(mbc);
+ return this;
+ }
+ public Builder setMbcAllChannelsTo(Mbc mbc) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setMbcByChannelIndex(ch, mbc);
+ }
+ return this;
+ }
+
+ public Builder setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setPostEq(postEq);
+ return this;
+ }
+ public Builder setPostEqAllChannelsTo(Eq postEq) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setPostEqByChannelIndex(ch, postEq);
+ }
+ return this;
+ }
+
+ public Builder setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+ checkChannel(channelIndex);
+ mChannel[channelIndex].setLimiter(limiter);
+ return this;
+ }
+ public Builder setLimiterAllChannelsTo(Limiter limiter) {
+ for (int ch = 0; ch < mChannel.length; ch++) {
+ setLimiterByChannelIndex(ch, limiter);
+ }
+ return this;
+ }
+
+ public Config build() {
+ return new Config(mVariant, mPreferredFrameDuration, mChannelCount,
+ mPreEqInUse, mPreEqBandCount,
+ mMbcInUse, mMbcBandCount,
+ mPostEqInUse, mPostEqBandCount,
+ mLimiterInUse, mChannel);
+ }
+ }
+ }
+ //=== CHANNEL
+ public Channel getChannelByChannelIndex(int channelIndex) {
+ return mConfig.getChannelByChannelIndex(channelIndex);
+ }
+
+ public void setChannelTo(int channelIndex, Channel channel) {
+ mConfig.setChannelTo(channelIndex, channel);
+ }
+
+ public void setAllChannelsTo(Channel channel) {
+ mConfig.setAllChannelsTo(channel);
+ }
+
+ //=== channel params
+ public float getInputGainByChannelIndex(int channelIndex) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getInputGainByChannelIndex(channelIndex);
+ }
+ public void setInputGainbyChannel(int channelIndex, float inputGain) {
+ mConfig.setInputGainByChannelIndex(channelIndex, inputGain);
+ //TODO: communicate change to engine
+ }
+ public void setInputGainAllChannelsTo(float inputGain) {
+ mConfig.setInputGainAllChannelsTo(inputGain);
+ //TODO: communicate change to engine
+ }
+
+ //=== PreEQ
+ public Eq getPreEqByChannelIndex(int channelIndex) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getPreEqByChannelIndex(channelIndex);
+ }
+
+ public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+ mConfig.setPreEqByChannelIndex(channelIndex, preEq);
+ //TODO: communicate change to engine
+ }
+
+ public void setPreEqAllChannelsTo(Eq preEq) {
+ mConfig.setPreEqAllChannelsTo(preEq);
+ //TODO: communicate change to engine
+ }
+
+ public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getPreEqBandByChannelIndex(channelIndex, band);
+ }
+
+ public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
+ mConfig.setPreEqBandByChannelIndex(channelIndex, band, preEqBand);
+ //TODO: communicate change to engine
+ }
+
+ public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
+ mConfig.setPreEqBandAllChannelsTo(band, preEqBand);
+ //TODO: communicate change to engine
+ }
+
+ //=== MBC
+ public Mbc getMbcByChannelIndex(int channelIndex) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getMbcByChannelIndex(channelIndex);
+ }
+
+ public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+ mConfig.setMbcByChannelIndex(channelIndex, mbc);
+ //TODO: communicate change to engine
+ }
+
+ public void setMbcAllChannelsTo(Mbc mbc) {
+ mConfig.setMbcAllChannelsTo(mbc);
+ //TODO: communicate change to engine
+ }
+
+ public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getMbcBandByChannelIndex(channelIndex, band);
+ }
+
+ public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
+ mConfig.setMbcBandByChannelIndex(channelIndex, band, mbcBand);
+ //TODO: communicate change to engine
+ }
+
+ public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
+ mConfig.setMbcBandAllChannelsTo(band, mbcBand);
+ //TODO: communicate change to engine
+ }
+
+ //== PostEq
+ public Eq getPostEqByChannelIndex(int channelIndex) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getPostEqByChannelIndex(channelIndex);
+ }
+
+ public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+ mConfig.setPostEqByChannelIndex(channelIndex, postEq);
+ //TODO: communicate change to engine
+ }
+
+ public void setPostEqAllChannelsTo(Eq postEq) {
+ mConfig.setPostEqAllChannelsTo(postEq);
+ //TODO: communicate change to engine
+ }
+
+ public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getPostEqBandByChannelIndex(channelIndex, band);
+ }
+
+ public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
+ mConfig.setPostEqBandByChannelIndex(channelIndex, band, postEqBand);
+ //TODO: communicate change to engine
+ }
+
+ public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
+ mConfig.setPostEqBandAllChannelsTo(band, postEqBand);
+ //TODO: communicate change to engine
+ }
+
+ //==== Limiter
+ public Limiter getLimiterByChannelIndex(int channelIndex) {
+ //TODO: return info from engine instead of cached config
+ return mConfig.getLimiterByChannelIndex(channelIndex);
+ }
+
+ public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+ mConfig.setLimiterByChannelIndex(channelIndex, limiter);
+ //TODO: communicate change to engine
+ }
+
+ public void setLimiterAllChannelsTo(Limiter limiter) {
+ mConfig.setLimiterAllChannelsTo(limiter);
+ //TODO: communicate change to engine
+ }
+
+ /**
+ * Gets the number of channels in the effect engine
+ * @return number of channels currently in use by the effect engine
+ */
+ public int getChannelCount() {
+ return getOneInt(PARAM_GET_CHANNEL_COUNT);
+ }
+
+ private void setEngineArchitecture(int variant, boolean preEqInUse, int preEqBandCount,
+ boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount,
+ boolean limiterInUse) {
+ int[] values = { variant, (preEqInUse ? 1 : 0), preEqBandCount,
+ (mbcInUse ? 1 : 0), mbcBandCount, (postEqInUse ? 1 : 0), postEqBandCount,
+ (limiterInUse ? 1 : 0)};
+ //TODO: enable later setIntArray(PARAM_SET_ENGINE_ARCHITECTURE, values);
+ }
+
+ //****** convenience methods:
+ //
+ private int getOneInt(int paramGet) {
+ int[] param = new int[1];
+ int[] result = new int[1];
+
+ param[0] = paramGet;
+ checkStatus(getParameter(param, result));
+ return result[0];
+ }
+
+ private int getTwoInt(int paramGet, int paramA) {
+ int[] param = new int[2];
+ int[] result = new int[1];
+
+ param[0] = paramGet;
+ param[1] = paramA;
+ checkStatus(getParameter(param, result));
+ return result[0];
+ }
+
+ private int getThreeInt(int paramGet, int paramA, int paramB) {
+ //have to use bytearrays, with more than 2 parameters.
+ byte[] paramBytes = concatArrays(intToByteArray(paramGet),
+ intToByteArray(paramA),
+ intToByteArray(paramB));
+ byte[] resultBytes = new byte[4]; //single int
+
+ checkStatus(getParameter(paramBytes, resultBytes));
+
+ return byteArrayToInt(resultBytes);
+ }
+
+ private void setOneInt(int paramSet, int valueSet) {
+ int[] param = new int[1];
+ int[] value = new int[1];
+
+ param[0] = paramSet;
+ value[0] = valueSet;
+ checkStatus(setParameter(param, value));
+ }
+
+ private void setTwoInt(int paramSet, int paramA, int valueSet) {
+ int[] param = new int[2];
+ int[] value = new int[1];
+
+ param[0] = paramSet;
+ param[1] = paramA;
+ value[0] = valueSet;
+ checkStatus(setParameter(param, value));
+ }
+
+ private void setThreeInt(int paramSet, int paramA, int paramB, int valueSet) {
+ //have to use bytearrays, with more than 2 parameters.
+ byte[] paramBytes = concatArrays(intToByteArray(paramSet),
+ intToByteArray(paramA),
+ intToByteArray(paramB));
+ byte[] valueBytes = intToByteArray(valueSet);
+
+ checkStatus(setParameter(paramBytes, valueBytes));
+ }
+
+ private void setOneFloat(int paramSet, float valueSet) {
+ int[] param = new int[1];
+ byte[] value;
+
+ param[0] = paramSet;
+ value = floatToByteArray(valueSet);
+ checkStatus(setParameter(param, value));
+ }
+
+ private void setTwoFloat(int paramSet, int paramA, float valueSet) {
+ int[] param = new int[2];
+ byte[] value;
+
+ param[0] = paramSet;
+ param[1] = paramA;
+ value = floatToByteArray(valueSet);
+ checkStatus(setParameter(param, value));
+ }
+
+ private void setThreeFloat(int paramSet, int paramA, int paramB, float valueSet) {
+ //have to use bytearrays, with more than 2 parameters.
+ byte[] paramBytes = concatArrays(intToByteArray(paramSet),
+ intToByteArray(paramA),
+ intToByteArray(paramB));
+ byte[] valueBytes = floatToByteArray(valueSet);
+
+ checkStatus(setParameter(paramBytes, valueBytes));
+ }
+ private byte[] intArrayToByteArray(int[] values) {
+ int expectedBytes = values.length * 4;
+ ByteBuffer converter = ByteBuffer.allocate(expectedBytes);
+ converter.order(ByteOrder.nativeOrder());
+ for (int k = 0; k < values.length; k++) {
+ converter.putFloat(values[k]);
+ }
+ return converter.array();
+ }
+ private void setIntArray(int paramSet, int[] paramArray) {
+ //have to use bytearrays, with more than 2 parameters.
+ byte[] paramBytes = intToByteArray(paramSet);
+ byte[] valueBytes = intArrayToByteArray(paramArray);
+
+ checkStatus(setParameter(paramBytes, valueBytes));
+ }
+
+ private float getOneFloat(int paramGet) {
+ int[] param = new int[1];
+ byte[] result = new byte[4];
+
+ param[0] = paramGet;
+ checkStatus(getParameter(param, result));
+ return byteArrayToFloat(result);
+ }
+
+ private float getTwoFloat(int paramGet, int paramA) {
+ int[] param = new int[2];
+ byte[] result = new byte[4];
+
+ param[0] = paramGet;
+ param[1] = paramA;
+ checkStatus(getParameter(param, result));
+ return byteArrayToFloat(result);
+ }
+
+ private float getThreeFloat(int paramGet, int paramA, int paramB) {
+ //have to use bytearrays, with more than 2 parameters.
+ byte[] paramBytes = concatArrays(intToByteArray(paramGet),
+ intToByteArray(paramA),
+ intToByteArray(paramB));
+ byte[] resultBytes = new byte[4]; //single float
+
+ checkStatus(getParameter(paramBytes, resultBytes));
+
+ return byteArrayToFloat(resultBytes);
+ }
+
+ private float[] getOneFloatArray(int paramGet, int expectedSize) {
+ int[] param = new int[1];
+ byte[] result = new byte[4 * expectedSize];
+
+ param[0] = paramGet;
+ checkStatus(getParameter(param, result));
+ float[] returnArray = new float[expectedSize];
+ for (int k = 0; k < expectedSize; k++) {
+ returnArray[k] = byteArrayToFloat(result, 4 * k);
+ }
+ return returnArray;
+ }
+ /**
+ * @hide
+ * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * DynamicsProcessing engine.
+ * @param effect the DynamicsProcessing on which the interface is registered.
+ * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(DynamicsProcessing effect, int param, int value);
+ }
+
+ /**
+ * helper method to update effect architecture parameters
+ */
+ private void updateEffectArchitecture() {
+ mChannelCount = getChannelCount();
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ // only notify when the parameter was successfully change
+ if (status != AudioEffect.SUCCESS) {
+ return;
+ }
+ OnParameterChangeListener l = null;
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ int v = Integer.MIN_VALUE;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+ if (p != -1 && v != Integer.MIN_VALUE) {
+ l.onParameterChange(DynamicsProcessing.this, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ mParamListener = listener;
+ }
+ }
+
+ /**
+ * @hide
+ * The Settings class regroups the DynamicsProcessing parameters. It is used in
+ * conjunction with the getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+
+ public static class Settings {
+ public int channelCount;
+ public float[] inputGain;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ //int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("DynamicsProcessing")) {
+ throw new IllegalArgumentException(
+ "invalid settings for DynamicsProcessing: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("channelCount")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ channelCount = Short.parseShort(st.nextToken());
+ if (channelCount > CHANNEL_COUNT_MAX) {
+ throw new IllegalArgumentException("too many channels Settings:" + settings);
+ }
+ if (st.countTokens() != channelCount*1) { //check expected parameters.
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ //check to see it is ok the size
+ inputGain = new float[channelCount];
+ for (int ch = 0; ch < channelCount; ch++) {
+ key = st.nextToken();
+ if (!key.equals(ch +"_inputGain")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ inputGain[ch] = Float.parseFloat(st.nextToken());
+ }
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "DynamicsProcessing"+
+ ";channelCount="+Integer.toString(channelCount));
+ for (int ch = 0; ch < channelCount; ch++) {
+ str = str.concat(";"+ch+"_inputGain="+Float.toString(inputGain[ch]));
+ }
+ return str;
+ }
+ };
+
+
+ /**
+ * @hide
+ * Gets the DynamicsProcessing properties. This method is useful when a snapshot of current
+ * effect settings must be saved by the application.
+ * @return a DynamicsProcessing.Settings object containing all current parameters values
+ */
+ public DynamicsProcessing.Settings getProperties() {
+ Settings settings = new Settings();
+
+ //TODO: just for testing, we are calling the getters one by one, this is
+ // supposed to be done in a single (or few calls) and get all the parameters at once.
+
+ settings.channelCount = getChannelCount();
+
+ if (settings.channelCount > CHANNEL_COUNT_MAX) {
+ throw new IllegalArgumentException("too many channels Settings:" + settings);
+ }
+
+ { // get inputGainmB per channel
+ settings.inputGain = new float [settings.channelCount];
+ for (int ch = 0; ch < settings.channelCount; ch++) {
+//TODO:with config settings.inputGain[ch] = getInputGain(ch);
+ }
+ }
+ return settings;
+ }
+
+ /**
+ * @hide
+ * Sets the DynamicsProcessing properties. This method is useful when bass boost settings
+ * have to be applied from a previous backup.
+ * @param settings a DynamicsProcessing.Settings object containing the properties to apply
+ */
+ public void setProperties(DynamicsProcessing.Settings settings) {
+
+ if (settings.channelCount != settings.inputGain.length ||
+ settings.channelCount != mChannelCount) {
+ throw new IllegalArgumentException("settings invalid channel count: "
+ + settings.channelCount);
+ }
+
+ //TODO: for now calling multiple times.
+ for (int ch = 0; ch < mChannelCount; ch++) {
+//TODO: use config setInputGain(ch, settings.inputGain[ch]);
+ }
+ }
+}
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index ebde3fef..8e69653 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -16,10 +16,10 @@
package android.media.update;
-import android.annotation.SystemApi;
+import android.media.SessionToken2;
import android.media.session.MediaController;
import android.util.AttributeSet;
-import android.view.View;
+import android.widget.MediaControlView2;
/**
* Interface for connecting the public API to an updatable implementation.
@@ -34,11 +34,19 @@
*
* @hide
*/
-// TODO @SystemApi
+// TODO: @SystemApi
public interface MediaControlView2Provider extends ViewGroupProvider {
void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+ void setMediaSessionToken_impl(SessionToken2 token);
+ void setOnFullScreenListener_impl(MediaControlView2.OnFullScreenListener l);
+ /**
+ * @hide TODO: remove
+ */
void setController_impl(MediaController controller);
+ /**
+ * @hide
+ */
void setButtonVisibility_impl(int button, int visibility);
void requestPlayButtonFocus_impl();
}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index ca5c16d..6d1b1b1 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -16,6 +16,7 @@
package android.media.update;
+import android.annotation.NonNull;
import android.app.PendingIntent;
import android.media.AudioAttributes;
import android.media.MediaController2.PlaybackInfo;
@@ -60,6 +61,7 @@
void removePlaylistItem_impl(MediaItem2 index);
void addPlaylistItem_impl(int index, MediaItem2 item);
+ void replacePlaylistItem_impl(int index, MediaItem2 item);
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index dbd4a0a..d0b11b7 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -16,11 +16,12 @@
package android.media.update;
+import android.annotation.NonNull;
import android.app.PendingIntent;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase.PlayerEventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -57,13 +58,14 @@
void addPlaylistItem_impl(int index, MediaItem2 item);
void removePlaylistItem_impl(MediaItem2 item);
void editPlaylistItem_impl(MediaItem2 item);
+ void replacePlaylistItem_impl(int index, MediaItem2 item);
List<MediaItem2> getPlaylist_impl();
MediaItem2 getCurrentPlaylistItem_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaylistParams getPlaylistParams_impl();
void notifyError_impl(int errorCode, int extra);
- void registerPlayerEventCallback_impl(Executor executor, EventCallback callback);
- void unregisterPlayerEventCallback_impl(EventCallback callback);
+ void registerPlayerEventCallback_impl(Executor executor, PlayerEventCallback callback);
+ void unregisterPlayerEventCallback_impl(PlayerEventCallback callback);
interface CommandProvider {
int getCommandCode_impl();
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 152ace9..11b3560 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -18,8 +18,11 @@
import android.annotation.SystemApi;
import android.media.AudioAttributes;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
+import android.media.SessionToken2;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
@@ -53,7 +56,11 @@
void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs);
void setMediaMetadata_impl(MediaMetadata2 metadata);
+ /**
+ * @hide TODO: remove
+ */
MediaController getMediaController_impl();
+ SessionToken2 getMediaSessionToken_impl();
MediaControlView2 getMediaControlView2_impl();
MediaMetadata2 getMediaMetadata_impl();
void setSubtitleEnabled_impl(boolean enable);
@@ -66,13 +73,31 @@
* @hide
*/
void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
+ /**
+ * @hide
+ */
// TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
+
+ /**
+ * @hide TODO: remove
+ */
void setVideoPath_impl(String path);
+ /**
+ * @hide TODO: remove
+ */
void setVideoUri_impl(Uri uri);
+ /**
+ * @hide TODO: remove
+ */
void setVideoUri_impl(Uri uri, Map<String, String> headers);
+ void setMediaItem_impl(MediaItem2 mediaItem);
+ void setDataSource_impl(DataSourceDesc dsd);
void setViewType_impl(int viewType);
int getViewType_impl();
+ /**
+ * @hide TODO: remove
+ */
void setCustomActions_impl(List<PlaybackState.CustomAction> actionList,
Executor executor, VideoView2.OnCustomActionListener listener);
/**
@@ -80,5 +105,8 @@
*/
@VisibleForTesting
void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l);
+ /**
+ * @hide TODO: remove
+ */
void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l);
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3c8af8a..44e5d61 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,6 +31,7 @@
shared_libs: [
"libandroid_runtime",
"libnativehelper",
+ "libnativewindow",
"libutils",
"libbinder",
"libmedia",
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 885bf03..f5311764 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -33,11 +33,14 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <grallocusage/GrallocUsageConversion.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <stdint.h>
#include <inttypes.h>
+#include <android/hardware_buffer_jni.h>
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
@@ -797,6 +800,14 @@
}
}
+static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
+ AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
+ // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
+ // to link against libandroid.so
+ return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
+}
+
} // extern "C"
// ----------------------------------------------------------------------------
@@ -814,10 +825,12 @@
static const JNINativeMethod gImageMethods[] = {
{"nativeCreatePlanes", "(II)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
- (void*)Image_createSurfacePlanes },
- {"nativeGetWidth", "()I", (void*)Image_getWidth },
- {"nativeGetHeight", "()I", (void*)Image_getHeight },
- {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
+ (void*)Image_createSurfacePlanes },
+ {"nativeGetWidth", "()I", (void*)Image_getWidth },
+ {"nativeGetHeight", "()I", (void*)Image_getHeight },
+ {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
+ {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
+ (void*)Image_getHardwareBuffer },
};
int register_android_media_ImageReader(JNIEnv *env) {
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
index e88c0f1..63f9f91 100644
--- a/media/lib/remotedisplay/Android.mk
+++ b/media/lib/remotedisplay/Android.mk
@@ -42,3 +42,24 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.media.remotedisplay.stubs-gen
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.media.remotedisplay.stubs_intermediates/src
+LOCAL_DROIDDOC_OPTIONS:= \
+ -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
+ -stubpackages com.android.media.remotedisplay \
+ -nodocs
+LOCAL_UNINSTALLABLE_MODULE := true
+include $(BUILD_DROIDDOC)
+com_android_media_remotedisplay_gen_stamp := $(full_target)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.media.remotedisplay.stubs
+LOCAL_SDK_VERSION := current
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_media_remotedisplay_gen_stamp)
+com_android_media_remotedisplay_gen_stamp :=
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media/lib/remotedisplay/README.txt b/media/lib/remotedisplay/README.txt
index 5738dbe..1a52c4d 100644
--- a/media/lib/remotedisplay/README.txt
+++ b/media/lib/remotedisplay/README.txt
@@ -1,8 +1,17 @@
-This library (com.android.media.remotedisplay.jar) is a shared java library
+There are two libraries defined in this directory:
+First, com.android.media.remotedisplay.jar is a shared java library
containing classes required by unbundled remote display providers.
+Second, com.android.media.remotedisplay.stubs.jar is a stub for the shared
+library which provides build-time APIs to the unbundled clients.
+
+At runtime, the shared library is added to the classloader of the app via the
+<uses-library> tag. And since Java always tries to load a class from the
+parent classloader, regardless of whether the stub library is linked to the
+app statically or dynamically, the real classes are loaded from the shared
+library.
--- Rules of this library ---
-o This library is effectively a PUBLIC API for unbundled remote display providers
+o The stub library is effectively a PUBLIC API for unbundled remote display providers
that may be distributed outside the system image. So it MUST BE API STABLE.
You can add but not remove. The rules are the same as for the
public platform SDK API.
diff --git a/media/mca/samples/CameraEffectsRecordingSample/Android.mk b/media/mca/samples/CameraEffectsRecordingSample/Android.mk
index d3c4336..c81f2fc 100644
--- a/media/mca/samples/CameraEffectsRecordingSample/Android.mk
+++ b/media/mca/samples/CameraEffectsRecordingSample/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := CameraEffectsRecordingSample
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/media/mca/tests/Android.mk b/media/mca/tests/Android.mk
index 394f542..648af4e 100644
--- a/media/mca/tests/Android.mk
+++ b/media/mca/tests/Android.mk
@@ -11,6 +11,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CameraEffectsTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := CameraEffectsRecordingSample
diff --git a/media/packages/BluetoothMidiService/Android.mk b/media/packages/BluetoothMidiService/Android.mk
index 0565925..6f262bf 100644
--- a/media/packages/BluetoothMidiService/Android.mk
+++ b/media/packages/BluetoothMidiService/Android.mk
@@ -7,6 +7,7 @@
$(call all-java-files-under,src)
LOCAL_PACKAGE_NAME := BluetoothMidiService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/media/tests/EffectsTest/Android.mk b/media/tests/EffectsTest/Android.mk
index 25b4fe4..a066950 100644
--- a/media/tests/EffectsTest/Android.mk
+++ b/media/tests/EffectsTest/Android.mk
@@ -6,5 +6,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := EffectsTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 145cde6..fb473f0 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -13,5 +13,6 @@
android-ex-camera2
LOCAL_PACKAGE_NAME := mediaframeworktest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/media/tests/MtpTests/Android.mk b/media/tests/MtpTests/Android.mk
index 616e600..6375ed3 100644
--- a/media/tests/MtpTests/Android.mk
+++ b/media/tests/MtpTests/Android.mk
@@ -8,5 +8,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := MtpTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/media/tests/NativeMidiDemo/Android.mk b/media/tests/NativeMidiDemo/Android.mk
index 6b08f6b..316858f 100644
--- a/media/tests/NativeMidiDemo/Android.mk
+++ b/media/tests/NativeMidiDemo/Android.mk
@@ -19,6 +19,7 @@
LOCAL_PACKAGE_NAME := NativeMidiDemo
#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_SRC_FILES := $(call all-java-files-under, java)
diff --git a/media/tests/ScoAudioTest/Android.mk b/media/tests/ScoAudioTest/Android.mk
index ab12865..2ad91a4 100644
--- a/media/tests/ScoAudioTest/Android.mk
+++ b/media/tests/ScoAudioTest/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/media/tests/SoundPoolTest/Android.mk b/media/tests/SoundPoolTest/Android.mk
index 7f947c0..9ca33c8 100644
--- a/media/tests/SoundPoolTest/Android.mk
+++ b/media/tests/SoundPoolTest/Android.mk
@@ -6,5 +6,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SoundPoolTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 137b72c..bf15b8d 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -21,7 +21,7 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk
index b84c07f..532d272 100644
--- a/packages/BackupRestoreConfirmation/Android.mk
+++ b/packages/BackupRestoreConfirmation/Android.mk
@@ -22,6 +22,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := BackupRestoreConfirmation
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/CompanionDeviceManager/Android.mk b/packages/CompanionDeviceManager/Android.mk
index f730356..7ec6e11 100644
--- a/packages/CompanionDeviceManager/Android.mk
+++ b/packages/CompanionDeviceManager/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CompanionDeviceManager
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 7e23ee1..16ef59f 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -41,7 +41,8 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "DeviceChooserActivity";
- private ListView mDeviceListView;
+ View mLoadingIndicator = null;
+ ListView mDeviceListView;
private View mPairButton;
private View mCancelButton;
@@ -80,8 +81,9 @@
onSelectionUpdate();
}
});
- mDeviceListView.addFooterView(getProgressBar(), null, false);
+ mDeviceListView.addFooterView(mLoadingIndicator = getProgressBar(), null, false);
}
+ getService().mActivity = this;
mCancelButton = findViewById(R.id.button_cancel);
mCancelButton.setOnClickListener(v -> cancel());
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 1e26231..a5f0f24 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -22,6 +22,7 @@
import static com.android.internal.util.ArrayUtils.isEmpty;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.CollectionUtils.size;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,6 +51,7 @@
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.wifi.WifiManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -63,7 +65,9 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -73,6 +77,8 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "DeviceDiscoveryService";
+ private static final long SCAN_TIMEOUT = 20000;
+
static DeviceDiscoveryService sInstance;
private BluetoothAdapter mBluetoothAdapter;
@@ -93,6 +99,8 @@
IFindDeviceCallback mFindCallback;
ICompanionDeviceDiscoveryServiceCallback mServiceCallback;
+ boolean mIsScanning = false;
+ @Nullable DeviceChooserActivity mActivity = null;
private final ICompanionDeviceDiscoveryService mBinder =
new ICompanionDeviceDiscoveryService.Stub() {
@@ -196,6 +204,10 @@
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mWifiManager.startScan();
}
+ mIsScanning = true;
+ Handler.getMain().sendMessageDelayed(
+ obtainMessage(DeviceDiscoveryService::stopScan, this),
+ SCAN_TIMEOUT);
}
private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) {
@@ -219,6 +231,15 @@
private void stopScan() {
if (DEBUG) Log.i(LOG_TAG, "stopScan()");
+ if (!mIsScanning) return;
+ mIsScanning = false;
+
+ DeviceChooserActivity activity = mActivity;
+ if (activity != null) {
+ activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator);
+ mActivity = null;
+ }
+
mBluetoothAdapter.cancelDiscovery();
if (mBluetoothBroadcastReceiver != null) {
unregisterReceiver(mBluetoothBroadcastReceiver);
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
index 0de2c1f..01c8768 100644
--- a/packages/DefaultContainerService/Android.mk
+++ b/packages/DefaultContainerService/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := DefaultContainerService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni
diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk
index a825581..605a75d 100644
--- a/packages/EasterEgg/Android.mk
+++ b/packages/EasterEgg/Android.mk
@@ -21,6 +21,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := EasterEgg
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/packages/ExtServices/Android.mk b/packages/ExtServices/Android.mk
index d0c2b9f..467d7ed 100644
--- a/packages/ExtServices/Android.mk
+++ b/packages/ExtServices/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ExtServices
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
index 1eb5847..0a95b85 100644
--- a/packages/ExtServices/tests/Android.mk
+++ b/packages/ExtServices/tests/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ExtServicesUnitTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := ExtServices
diff --git a/packages/ExtShared/Android.mk b/packages/ExtShared/Android.mk
index d8052df..7dbf79f 100644
--- a/packages/ExtShared/Android.mk
+++ b/packages/ExtShared/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ExtShared
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk
index db825ff4..9e99313 100644
--- a/packages/ExternalStorageProvider/Android.mk
+++ b/packages/ExternalStorageProvider/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := ExternalStorageProvider
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/FakeOemFeatures/Android.mk b/packages/FakeOemFeatures/Android.mk
index d96bb3d..43de8e5 100644
--- a/packages/FakeOemFeatures/Android.mk
+++ b/packages/FakeOemFeatures/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := FakeOemFeatures
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk
index 7406eaf4..d795870 100644
--- a/packages/FusedLocation/Android.mk
+++ b/packages/FusedLocation/Android.mk
@@ -22,6 +22,7 @@
LOCAL_JAVA_LIBRARIES := com.android.location.provider
LOCAL_PACKAGE_NAME := FusedLocation
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
index e7190dc..6de1f1d 100644
--- a/packages/InputDevices/Android.mk
+++ b/packages/InputDevices/Android.mk
@@ -22,6 +22,7 @@
LOCAL_JAVA_LIBRARIES :=
LOCAL_PACKAGE_NAME := InputDevices
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
index a9e9b2e..2d62a07 100644
--- a/packages/MtpDocumentsProvider/Android.mk
+++ b/packages/MtpDocumentsProvider/Android.mk
@@ -4,6 +4,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := MtpDocumentsProvider
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := media
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index f0d4878..6504af1 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -5,6 +5,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := MtpDocumentsProviderPerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
diff --git a/packages/MtpDocumentsProvider/tests/Android.mk b/packages/MtpDocumentsProvider/tests/Android.mk
index ba346f42..11daac3 100644
--- a/packages/MtpDocumentsProvider/tests/Android.mk
+++ b/packages/MtpDocumentsProvider/tests/Android.mk
@@ -6,6 +6,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := MtpDocumentsProviderTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/packages/Osu/Android.mk b/packages/Osu/Android.mk
index 1d45aa9..63c7578 100644
--- a/packages/Osu/Android.mk
+++ b/packages/Osu/Android.mk
@@ -14,6 +14,7 @@
LOCAL_JAVA_LIBRARIES := telephony-common ims-common bouncycastle conscrypt
LOCAL_PACKAGE_NAME := Osu
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/Osu2/Android.mk b/packages/Osu2/Android.mk
index 05586f0..063ac7e 100644
--- a/packages/Osu2/Android.mk
+++ b/packages/Osu2/Android.mk
@@ -9,6 +9,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Osu2
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/Osu2/tests/Android.mk b/packages/Osu2/tests/Android.mk
index afc743d..23db7a9 100644
--- a/packages/Osu2/tests/Android.mk
+++ b/packages/Osu2/tests/Android.mk
@@ -25,6 +25,7 @@
LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_PACKAGE_NAME := OsuTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_INSTRUMENTATION_FOR := Osu2
diff --git a/packages/PrintRecommendationService/Android.mk b/packages/PrintRecommendationService/Android.mk
index 66cb057..1220349 100644
--- a/packages/PrintRecommendationService/Android.mk
+++ b/packages/PrintRecommendationService/Android.mk
@@ -21,9 +21,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := PrintRecommendationService
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
-LOCAL_SDK_VERSION := system_current
-
include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 6feb8a6..e356f38 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -26,6 +26,7 @@
src/com/android/printspooler/renderer/IPdfEditor.aidl
LOCAL_PACKAGE_NAME := PrintSpooler
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JNI_SHARED_LIBRARIES := libprintspooler_jni
LOCAL_STATIC_ANDROID_LIBRARIES := \
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
index 149be74..161a600 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.mk
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -20,10 +20,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4 print-test-util-lib
LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_PACKAGE)
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 58d5db3..a75b147 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -876,47 +876,53 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration">About <xliff:g id="time">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+ <string name="power_discharging_duration_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery -->
+ <string name="power_discharge_by_enhanced">Will last until about about <xliff:g id="time">%1$s</xliff:g> based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery with no percentage -->
+ <string name="power_discharge_by_only_enhanced">Will last until about about <xliff:g id="time">%1$s</xliff:g> based on your usage</string>
+ <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
+ <string name="power_discharge_by">Will last until about about <xliff:g id="time">%1$s</xliff:g> (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
+ <string name="power_discharge_by_only">Will last until about about <xliff:g id="time">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
- <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
- <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+ <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+ <string name="power_remaining_less_than_duration">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string>
- <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
- <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
- <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
- <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+ <string name="power_remaining_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+ <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
-
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
-
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 9947dec..61e113b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -142,7 +142,7 @@
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_badge);
+ Drawable drawable = UserIconDrawable.getManagedUserBadgeDrawable(context);
drawable.setBounds(0, 0, iconSize, iconSize);
return drawable;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index d14b53b..660521e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -37,7 +37,16 @@
* Updates the current status of preference (summary, switch state, etc)
*/
public void updateState(Preference preference) {
-
+ if (preference == null) {
+ return;
+ }
+ final CharSequence summary = getSummary();
+ if (summary == null) {
+ // Default getSummary returns null. If subclass didn't override this, there is nothing
+ // we need to do.
+ return;
+ }
+ preference.setSummary(summary);
}
/**
@@ -72,9 +81,9 @@
/**
- * @return a String for the summary of the preference.
+ * @return a {@link CharSequence} for the summary of the preference.
*/
- public String getSummary() {
+ public CharSequence getSummary() {
return null;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
index f68c04f..d3dc8aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -17,6 +17,8 @@
package com.android.settingslib.development;
import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -26,8 +28,9 @@
* All Preference Controllers that are a part of the developer options page should inherit this
* class.
*/
-public abstract class DeveloperOptionsPreferenceController extends
- AbstractPreferenceController {
+public abstract class DeveloperOptionsPreferenceController extends AbstractPreferenceController {
+
+ protected Preference mPreference;
public DeveloperOptionsPreferenceController(Context context) {
super(context);
@@ -43,6 +46,12 @@
return true;
}
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
/**
* Called when developer options is enabled
*/
@@ -65,12 +74,14 @@
* Called when developer options is enabled and the preference is available
*/
protected void onDeveloperOptionsSwitchEnabled() {
+ mPreference.setEnabled(true);
}
/**
* Called when developer options is disabled and the preference is available
*/
protected void onDeveloperOptionsSwitchDisabled() {
+ mPreference.setEnabled(false);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 346ca66..8b3da39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -17,22 +17,30 @@
package com.android.settingslib.utils;
import android.content.Context;
+import android.icu.text.DateFormat;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
-import com.android.settingslib.utils.StringUtil;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/** Utility class for keeping power related strings consistent**/
public class PowerUtil {
+
private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+ private static final long TWO_DAYS_MILLIS = TimeUnit.DAYS.toMillis(2);
+ private static final long ONE_HOUR_MILLIS = TimeUnit.HOURS.toMillis(1);
/**
* This method produces the text used in various places throughout the system to describe the
@@ -57,11 +65,15 @@
FIFTEEN_MINUTES_MILLIS,
false /* withSeconds */);
return getUnderFifteenString(context, timeString, percentageString);
+ } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
+ // just say more than two day if over 48 hours
+ return getMoreThanTwoDaysString(context, percentageString);
} else if (drainTimeMs >= ONE_DAY_MILLIS) {
- // just say more than one day if over 24 hours
- return getMoreThanOneDayString(context, percentageString);
+ // show remaining days & hours if more than a day
+ return getMoreThanOneDayString(context, drainTimeMs,
+ percentageString, basedOnUsage);
} else {
- // show a regular time remaining string
+ // show the time of day we think you'll run out
return getRegularTimeRemainingString(context, drainTimeMs,
percentageString, basedOnUsage);
}
@@ -83,34 +95,18 @@
? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
: context.getString(
R.string.power_remaining_less_than_duration,
- percentageString,
- timeString);
+ timeString,
+ percentageString);
}
- private static String getMoreThanOneDayString(Context context, String percentageString) {
- final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
- final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
-
- final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
-
- return TextUtils.isEmpty(percentageString)
- ? context.getString(R.string.power_remaining_only_more_than_subtext,
- frmt.formatMeasures(daysMeasure))
- : context.getString(
- R.string.power_remaining_more_than_subtext,
- percentageString,
- frmt.formatMeasures(daysMeasure));
- }
-
- private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ private static String getMoreThanOneDayString(Context context, long drainTimeMs,
String percentageString, boolean basedOnUsage) {
- // round to the nearest 15 min to not appear oversly precise
- final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
- FIFTEEN_MINUTES_MILLIS);
+ final long roundedTimeMs = roundToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
CharSequence timeString = StringUtil.formatElapsedTime(context,
roundedTimeMs,
false /* withSeconds */);
+
if (TextUtils.isEmpty(percentageString)) {
int id = basedOnUsage
? R.string.power_remaining_duration_only_enhanced
@@ -120,7 +116,48 @@
int id = basedOnUsage
? R.string.power_discharging_duration_enhanced
: R.string.power_discharging_duration;
- return context.getString(id, percentageString, timeString);
+ return context.getString(id, timeString, percentageString);
+ }
+ }
+
+ private static String getMoreThanTwoDaysString(Context context, String percentageString) {
+ final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+ final Measure daysMeasure = new Measure(2, MeasureUnit.DAY);
+
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_only_more_than_subtext,
+ frmt.formatMeasures(daysMeasure))
+ : context.getString(
+ R.string.power_remaining_more_than_subtext,
+ frmt.formatMeasures(daysMeasure),
+ percentageString);
+ }
+
+ private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ String percentageString, boolean basedOnUsage) {
+ // Get the time of day we think device will die rounded to the nearest 15 min.
+ final long roundedTimeOfDayMs =
+ roundToNearestThreshold(
+ System.currentTimeMillis() + drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+
+ // convert the time to a properly formatted string.
+ DateFormat fmt = DateFormat.getTimeInstance(DateFormat.SHORT);
+ Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
+ CharSequence timeString = fmt.format(date);
+
+ if (TextUtils.isEmpty(percentageString)) {
+ int id = basedOnUsage
+ ? R.string.power_discharge_by_only_enhanced
+ : R.string.power_discharge_by_only;
+ return context.getString(id, timeString);
+ } else {
+ int id = basedOnUsage
+ ? R.string.power_discharge_by_enhanced
+ : R.string.power_discharge_by;
+ return context.getString(id, timeString, percentageString);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 45fdd78..68be2b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -33,74 +33,74 @@
/** Utility class for generally useful string methods **/
public class StringUtil {
- public static final int SECONDS_PER_MINUTE = 60;
- public static final int SECONDS_PER_HOUR = 60 * 60;
- public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int SECONDS_PER_HOUR = 60 * 60;
+ public static final int SECONDS_PER_DAY = 24 * 60 * 60;
- /**
- * Returns elapsed time for the given millis, in the following format:
- * 2d 5h 40m 29s
- * @param context the application context
- * @param millis the elapsed time in milli seconds
- * @param withSeconds include seconds?
- * @return the formatted elapsed time
- */
- public static CharSequence formatElapsedTime(Context context, double millis,
- boolean withSeconds) {
- SpannableStringBuilder sb = new SpannableStringBuilder();
- int seconds = (int) Math.floor(millis / 1000);
- if (!withSeconds) {
- // Round up.
- seconds += 30;
- }
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 2d 5h 40m 29s
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatElapsedTime(Context context, double millis,
+ boolean withSeconds) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ int seconds = (int) Math.floor(millis / 1000);
+ if (!withSeconds) {
+ // Round up.
+ seconds += 30;
+ }
- int days = 0, hours = 0, minutes = 0;
- if (seconds >= SECONDS_PER_DAY) {
- days = seconds / SECONDS_PER_DAY;
- seconds -= days * SECONDS_PER_DAY;
- }
- if (seconds >= SECONDS_PER_HOUR) {
- hours = seconds / SECONDS_PER_HOUR;
- seconds -= hours * SECONDS_PER_HOUR;
- }
- if (seconds >= SECONDS_PER_MINUTE) {
- minutes = seconds / SECONDS_PER_MINUTE;
- seconds -= minutes * SECONDS_PER_MINUTE;
- }
+ int days = 0, hours = 0, minutes = 0;
+ if (seconds >= SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds >= SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds >= SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
- final ArrayList<Measure> measureList = new ArrayList(4);
- if (days > 0) {
- measureList.add(new Measure(days, MeasureUnit.DAY));
- }
- if (hours > 0) {
- measureList.add(new Measure(hours, MeasureUnit.HOUR));
- }
- if (minutes > 0) {
- measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
- }
- if (withSeconds && seconds > 0) {
- measureList.add(new Measure(seconds, MeasureUnit.SECOND));
- }
- if (measureList.size() == 0) {
- // Everything addable was zero, so nothing was added. We add a zero.
- measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
- }
- final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+ final ArrayList<Measure> measureList = new ArrayList(4);
+ if (days > 0) {
+ measureList.add(new Measure(days, MeasureUnit.DAY));
+ }
+ if (hours > 0) {
+ measureList.add(new Measure(hours, MeasureUnit.HOUR));
+ }
+ if (minutes > 0) {
+ measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+ if (withSeconds && seconds > 0) {
+ measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+ }
+ if (measureList.size() == 0) {
+ // Everything addable was zero, so nothing was added. We add a zero.
+ measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+ }
+ final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
- final Locale locale = context.getResources().getConfiguration().locale;
- final MeasureFormat measureFormat = MeasureFormat.getInstance(
- locale, FormatWidth.NARROW);
- sb.append(measureFormat.formatMeasures(measureArray));
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final MeasureFormat measureFormat = MeasureFormat.getInstance(
+ locale, FormatWidth.NARROW);
+ sb.append(measureFormat.formatMeasures(measureArray));
- if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
- // Add ttsSpan if it only have minute value, because it will be read as "meters"
- final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
- .setUnit("minute").build();
- sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+ // Add ttsSpan if it only have minute value, because it will be read as "meters"
+ final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+ .setUnit("minute").build();
+ sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
- return sb;
- }
+ return sb;
+ }
/**
* Returns relative time for the given millis in the past, in a short format such as "2 days
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 8767923..393fd02 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -82,8 +82,17 @@
assertThat(mPreference.isVisible()).isFalse();
}
- private class TestPrefController extends AbstractPreferenceController {
+ @Test
+ public void updateState_hasSummary_shouldSetSummary() {
+ mTestPrefController.updateState(mPreference);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TestPrefController.TEST_SUMMARY);
+ }
+
+ private static class TestPrefController extends AbstractPreferenceController {
private static final String KEY_PREF = "test_pref";
+ private static final CharSequence TEST_SUMMARY = "Test";
+
public boolean isAvailable;
public TestPrefController(Context context) {
@@ -104,6 +113,11 @@
public String getPreferenceKey() {
return KEY_PREF;
}
+
+ @Override
+ public CharSequence getSummary() {
+ return TEST_SUMMARY;
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
new file mode 100644
index 0000000..7820fd2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DeveloperOptionsPreferenceControllerTest {
+
+ private static final String TEST_KEY = "Test_pref_key";
+
+ @Mock
+ private Preference mPreference;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+
+ private DeveloperOptionsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DeveloperOptionsPreferenceControllerTestable();
+ doReturn(mPreference).when(mPreferenceScreen).findPreference(TEST_KEY);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void onDeveloperOptionsEnabled_shouldEnablePreference() {
+ mController.onDeveloperOptionsEnabled();
+
+ verify(mPreference).setEnabled(true);
+ }
+
+ @Test
+ public void onDeveloperOptionsDisabled_shouldDisablePreference() {
+ mController.onDeveloperOptionsDisabled();
+
+ verify(mPreference).setEnabled(false);
+ }
+
+ private class DeveloperOptionsPreferenceControllerTestable extends
+ DeveloperOptionsPreferenceController {
+ DeveloperOptionsPreferenceControllerTestable() {
+ super(RuntimeEnvironment.application);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return TEST_KEY;
+ }
+ }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 9285148f..c42ff08 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -24,13 +24,18 @@
import com.android.settingslib.R;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import java.time.Clock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSettings.ShadowSystem;
+import org.robolectric.shadows.ShadowSystemClock;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class PowerUtilTest {
@@ -39,8 +44,12 @@
public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
- public static final String ONE_DAY_FORMATTED = "1 day";
+ public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+ public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+ public static final String TWO_DAYS_FORMATTED = "2 days";
+ public static final String THIRTY_HOURS_FORMATTED = "1d 6h";
+ public static final String NORMAL_CASE_EXPECTED_PREFIX = "Will last until about";
+ public static final String ENHANCED_SUFFIX = "based on your usage";
private Context mContext;
@@ -51,6 +60,7 @@
}
@Test
+ @Config(shadows = {ShadowSystemClock.class})
public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
SEVENTEEN_MIN_MILLIS,
@@ -62,15 +72,13 @@
false /* basedOnUsage */);
// We only add special mention for the long string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_discharging_duration_enhanced,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info).contains(ENHANCED_SUFFIX);
+ assertThat(info).contains("%");
// shortened string should not have extra text
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_discharging_duration,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info2).doesNotContain(ENHANCED_SUFFIX);
+ assertThat(info2).contains("%");
}
@Test
@@ -84,14 +92,14 @@
null /* percentageString */,
false /* basedOnUsage */);
- // We only add special mention for the long string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only_enhanced,
- FIFTEEN_MIN_FORMATTED));
+ // We only have % when it is provided
+ assertThat(info).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info).contains(ENHANCED_SUFFIX);
+ assertThat(info).doesNotContain("%");
// shortened string should not have extra text
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).contains(NORMAL_CASE_EXPECTED_PREFIX);
+ assertThat(info2).doesNotContain(ENHANCED_SUFFIX);
+ assertThat(info2).doesNotContain("%");
}
@@ -107,12 +115,9 @@
true /* basedOnUsage */);
// additional battery percentage in this string
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_shutdown_imminent,
- TEST_BATTERY_LEVEL_10));
+ assertThat(info).isEqualTo("Phone may shutdown soon (10%)");
// shortened string should not have percentage
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_duration_only_shutdown_imminent));
+ assertThat(info2).isEqualTo("Phone may shutdown soon");
}
@Test
@@ -127,35 +132,42 @@
true /* basedOnUsage */);
// shortened string should not have percentage
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_less_than_duration_only,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info).isEqualTo("Less than 15m remaining");
// Add percentage to string when provided
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_less_than_duration,
- TEST_BATTERY_LEVEL_10,
- FIFTEEN_MIN_FORMATTED));
+ assertThat(info2).isEqualTo("Less than 15m remaining (10%)");
}
@Test
- public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+ public void testGetBatteryRemainingStringFormatted_betweenOneAndTwoDays_usesCorrectString() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TWO_DAYS_MILLIS,
+ THIRTY_HOURS_MILLIS,
null /* percentageString */,
true /* basedOnUsage */);
String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TWO_DAYS_MILLIS,
+ THIRTY_HOURS_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo("About 1d 6h left based on your usage");
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo("About 1d 6h left (10%)");
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanTwoDays_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ THREE_DAYS_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ THREE_DAYS_MILLIS,
TEST_BATTERY_LEVEL_10 /* percentageString */,
true /* basedOnUsage */);
// shortened string should not have percentage
- assertThat(info).isEqualTo(mContext.getString(
- R.string.power_remaining_only_more_than_subtext,
- ONE_DAY_FORMATTED));
+ assertThat(info).isEqualTo("More than 2 days remaining");
// Add percentage to string when provided
- assertThat(info2).isEqualTo(mContext.getString(
- R.string.power_remaining_more_than_subtext,
- TEST_BATTERY_LEVEL_10,
- ONE_DAY_FORMATTED));
+ assertThat(info2).isEqualTo("More than 2 days remaining (10%)");
}
}
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index 0f2c5ab..db57fd1 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -10,6 +10,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := SettingsProvider
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index 902f1c7..bd5b1f2 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -15,6 +15,7 @@
LOCAL_JAVA_LIBRARIES := android.test.base
LOCAL_PACKAGE_NAME := SettingsProviderTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
index a213965f..2e07ab1 100644
--- a/packages/SharedStorageBackup/Android.mk
+++ b/packages/SharedStorageBackup/Android.mk
@@ -24,6 +24,7 @@
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/Shell/Android.mk b/packages/Shell/Android.mk
index 935d09b..5713dc6 100644
--- a/packages/Shell/Android.mk
+++ b/packages/Shell/Android.mk
@@ -15,6 +15,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
LOCAL_PACKAGE_NAME := Shell
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk
index 7f24a38..b93ddde 100644
--- a/packages/Shell/tests/Android.mk
+++ b/packages/Shell/tests/Android.mk
@@ -15,6 +15,7 @@
junit \
LOCAL_PACKAGE_NAME := ShellTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_INSTRUMENTATION_FOR := Shell
diff --git a/packages/StatementService/Android.mk b/packages/StatementService/Android.mk
index 470d824..b9b29e7 100644
--- a/packages/StatementService/Android.mk
+++ b/packages/StatementService/Android.mk
@@ -22,6 +22,7 @@
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PACKAGE_NAME := StatementService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index f635b18..3c24845 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -43,40 +43,24 @@
android:layout_gravity="center_vertical"
android:gravity="end" >
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
- <!-- Add an extra 8dp margin before carrier text without shifting it right -->
- <android.widget.Space
- android:layout_width="8dp"
- android:layout_height="match_parent" />
-
- <com.android.keyguard.CarrierText
- android:id="@+id/qs_carrier_text"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical|start"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:textDirection="locale"
- android:singleLine="true" />
- </LinearLayout>
-
- <View
- android:id="@+id/qs_drag_handle_view"
- android:layout_width="24dp"
- android:layout_height="4dp"
- android:layout_gravity="center"
- android:background="@drawable/qs_footer_drag_handle" />
-
- <com.android.keyguard.AlphaOptimizedLinearLayout
- android:id="@+id/qs_footer_actions_container"
+ <com.android.keyguard.CarrierText
+ android:id="@+id/qs_carrier_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="32dp"
+ android:gravity="center_vertical|start"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textDirection="locale"
+ android:singleLine="true" />
+
+ <com.android.keyguard.AlphaOptimizedLinearLayout
+ android:id="@+id/qs_footer_actions_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:gravity="center_vertical|end" >
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
@@ -139,4 +123,11 @@
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
+ <View
+ android:id="@+id/qs_drag_handle_view"
+ android:layout_width="24dp"
+ android:layout_height="4dp"
+ android:layout_gravity="center"
+ android:background="@drawable/qs_footer_drag_handle" />
+
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 959247e..ca8fcba 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -44,6 +44,8 @@
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="@id/quick_qs_status_icons"
+ android:layout_marginStart="@dimen/qs_header_tile_margin_horizontal"
+ android:layout_marginEnd="@dimen/qs_header_tile_margin_horizontal"
android:accessibilityTraversalAfter="@+id/date_time_group"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 4614999..2e7ab7f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
+<!-- extends FrameLayout -->
<com.android.systemui.statusbar.ExpandableNotificationRow
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -54,6 +55,7 @@
android:paddingStart="8dp"
/>
+ <!-- TODO: remove -->
<ImageButton
android:id="@+id/helper"
android:layout_width="48dp"
@@ -64,7 +66,7 @@
android:tint="#FF0000"
android:background="@drawable/ripple_drawable"
android:visibility="visible"
- />
+ />
<ViewStub
android:layout="@layout/notification_children_container"
diff --git a/packages/SystemUI/res/values-sw372dp/dimens.xml b/packages/SystemUI/res/values-sw372dp/dimens.xml
index 635185d..3a7442a 100644
--- a/packages/SystemUI/res/values-sw372dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw372dp/dimens.xml
@@ -18,4 +18,5 @@
<resources>
<dimen name="nav_content_padding">8dp</dimen>
<dimen name="rounded_corner_content_padding">8dp</dimen>
+ <dimen name="qs_header_tile_margin_horizontal">5dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index dc230d4..ca4ea9e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -301,11 +301,13 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">106dp</dimen>
- <dimen name="qs_tile_margin">19dp</dimen>
+ <dimen name="qs_tile_margin_horizontal">18dp</dimen>
+ <dimen name="qs_tile_margin_vertical">24dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
+ <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3483e65..9245ac1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2138,4 +2138,8 @@
<!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
<string name="slice_permission_deny">Deny</string>
+ <!-- List of packages for which we don't want to show recents onboarding, add into overlay as needed. -->
+ <string-array name="recents_onboarding_blacklisted_packages" translatable="false">
+ </string-array>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index b8319a8e..846aadd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -28,25 +28,30 @@
* Proxies SurfaceControl.screenshotToBuffer().
*/
GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
- int maxLayer, boolean useIdentityTransform, int rotation);
+ int maxLayer, boolean useIdentityTransform, int rotation) = 0;
/**
* Begins screen pinning on the provided {@param taskId}.
*/
- void startScreenPinning(int taskId);
+ void startScreenPinning(int taskId) = 1;
/**
* Called when the overview service has started the recents animation.
*/
- void onRecentsAnimationStarted();
+ void onRecentsAnimationStarted() = 2;
/**
* Specifies the text to be shown for onboarding the new swipe-up gesture to access recents.
*/
- void setRecentsOnboardingText(CharSequence text);
+ void setRecentsOnboardingText(CharSequence text) = 3;
/**
* Enables/disables launcher/overview interaction features {@link InteractionType}.
*/
- void setInteractionState(int flags);
+ void setInteractionState(int flags) = 4;
+
+ /**
+ * Notifies SystemUI that split screen has been invoked.
+ */
+ void onSplitScreenInvoked() = 5;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 0103cad..2f28c81 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -403,6 +404,25 @@
}
/**
+ * @return whether screen pinning is active.
+ */
+ public boolean isScreenPinningActive() {
+ try {
+ return ActivityManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @return whether screen pinning is enabled.
+ */
+ public boolean isScreenPinningEnabled() {
+ final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
+ return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
+ }
+
+ /**
* @return whether there is currently a locked task (ie. in screen pinning).
*/
public boolean isLockToAppActive() {
@@ -415,9 +435,9 @@
/**
* @return whether screen pinning is enabled.
+ * @deprecated See {@link #isScreenPinningEnabled}
*/
public boolean isLockToAppEnabled() {
- final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
- return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
+ return isScreenPinningEnabled();
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 68400fc..5b49e67 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -101,4 +101,12 @@
Log.w(TAG, "Failed to override pending app transition (remote): ", e);
}
}
+
+ public void endProlongedAnimations() {
+ try {
+ WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to end prolonged animations: ", e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c3413d9..cb5a050 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
+import android.util.StatsLog;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
@@ -430,9 +431,13 @@
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
if (success) {
+ StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
monitor.clearFailedUnlockAttempts();
mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
} else {
+ StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7403ddc..cad155c 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -44,6 +44,7 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -314,6 +315,8 @@
mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+ mProviders.put(AppOpsListener.class, () -> new AppOpsListener(mContext));
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index a2c9ab4..5a2263c 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -14,7 +14,9 @@
package com.android.systemui;
+import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
public interface ForegroundServiceController {
/**
@@ -46,4 +48,32 @@
* @return true if sbn is the system-provided "dungeon" (list of running foreground services).
*/
boolean isDungeonNotification(StatusBarNotification sbn);
+
+ /**
+ * @return true if sbn is one of the window manager "drawing over other apps" notifications
+ */
+ boolean isSystemAlertNotification(StatusBarNotification sbn);
+
+ /**
+ * Returns the key of the foreground service from this package using the standard template,
+ * if one exists.
+ */
+ @Nullable String getStandardLayoutKey(int userId, String pkg);
+
+ /**
+ * @return true if this user/pkg has a missing or custom layout notification and therefore needs
+ * a disclosure notification for system alert windows.
+ */
+ boolean isSystemAlertWarningNeeded(int userId, String pkg);
+
+ /**
+ * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
+ * case they change before we have a notification to tag.
+ */
+ void onAppOpChanged(int code, int uid, String packageName, boolean active);
+
+ /**
+ * Gets active app ops for this user and package.
+ */
+ @Nullable ArraySet<Integer> getAppOps(int userId, String packageName);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
index 3714c4e..fc2b5b4 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
@@ -18,13 +18,13 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto;
import java.util.Arrays;
@@ -34,17 +34,19 @@
*/
public class ForegroundServiceControllerImpl
implements ForegroundServiceController {
-
+
// shelf life of foreground services before they go bad
public static final long FG_SERVICE_GRACE_MILLIS = 5000;
private static final String TAG = "FgServiceController";
private static final boolean DBG = false;
+ private final Context mContext;
private final SparseArray<UserServices> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
public ForegroundServiceControllerImpl(Context context) {
+ mContext = context;
}
@Override
@@ -57,6 +59,52 @@
}
@Override
+ public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) return false;
+ return services.getStandardLayoutKey(pkg) == null;
+ }
+ }
+
+ @Override
+ public String getStandardLayoutKey(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) return null;
+ return services.getStandardLayoutKey(pkg);
+ }
+ }
+
+ @Override
+ public ArraySet<Integer> getAppOps(int userId, String pkg) {
+ synchronized (mMutex) {
+ final UserServices services = mUserServices.get(userId);
+ if (services == null) {
+ return null;
+ }
+ return services.getFeatures(pkg);
+ }
+ }
+
+ @Override
+ public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
+ int userId = UserHandle.getUserId(uid);
+ synchronized (mMutex) {
+ UserServices userServices = mUserServices.get(userId);
+ if (userServices == null) {
+ userServices = new UserServices();
+ mUserServices.put(userId, userServices);
+ }
+ if (active) {
+ userServices.addOp(packageName, code);
+ } else {
+ userServices.removeOp(packageName, code);
+ }
+ }
+ }
+
+ @Override
public void addNotification(StatusBarNotification sbn, int importance) {
updateNotification(sbn, importance);
}
@@ -102,9 +150,16 @@
}
} else {
userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
- if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
- && newImportance > NotificationManager.IMPORTANCE_MIN) {
- userServices.addNotification(sbn.getPackageName(), sbn.getKey());
+ if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)) {
+ if (newImportance > NotificationManager.IMPORTANCE_MIN) {
+ userServices.addImportantNotification(sbn.getPackageName(), sbn.getKey());
+ }
+ final Notification.Builder builder = Notification.Builder.recoverBuilder(
+ mContext, sbn.getNotification());
+ if (builder.usesStandardHeader()) {
+ userServices.addStandardLayoutNotification(
+ sbn.getPackageName(), sbn.getKey());
+ }
}
}
}
@@ -117,42 +172,105 @@
&& sbn.getPackageName().equals("android");
}
+ @Override
+ public boolean isSystemAlertNotification(StatusBarNotification sbn) {
+ // TODO: tag system alert notifications so they can be suppressed if app's notification
+ // is tagged
+ return false;
+ }
+
/**
* Struct to track relevant packages and notifications for a userid's foreground services.
*/
private static class UserServices {
private String[] mRunning = null;
private long mServiceStartTime = 0;
- private ArrayMap<String, ArraySet<String>> mNotifications = new ArrayMap<>(1);
+ // package -> sufficiently important posted notification keys
+ private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
+ // package -> standard layout posted notification keys
+ private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
+
+ // package -> app ops
+ private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
+
public void setRunningServices(String[] pkgs, long serviceStartTime) {
mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
mServiceStartTime = serviceStartTime;
}
- public void addNotification(String pkg, String key) {
- if (mNotifications.get(pkg) == null) {
- mNotifications.put(pkg, new ArraySet<String>());
+
+ public void addOp(String pkg, int op) {
+ if (mAppOps.get(pkg) == null) {
+ mAppOps.put(pkg, new ArraySet<>(3));
}
- mNotifications.get(pkg).add(key);
+ mAppOps.get(pkg).add(op);
}
- public boolean removeNotification(String pkg, String key) {
+
+ public boolean removeOp(String pkg, int op) {
final boolean found;
- final ArraySet<String> keys = mNotifications.get(pkg);
+ final ArraySet<Integer> keys = mAppOps.get(pkg);
+ if (keys == null) {
+ found = false;
+ } else {
+ found = keys.remove(op);
+ if (keys.size() == 0) {
+ mAppOps.remove(pkg);
+ }
+ }
+ return found;
+ }
+
+ public void addImportantNotification(String pkg, String key) {
+ addNotification(mImportantNotifications, pkg, key);
+ }
+
+ public boolean removeImportantNotification(String pkg, String key) {
+ return removeNotification(mImportantNotifications, pkg, key);
+ }
+
+ public void addStandardLayoutNotification(String pkg, String key) {
+ addNotification(mStandardLayoutNotifications, pkg, key);
+ }
+
+ public boolean removeStandardLayoutNotification(String pkg, String key) {
+ return removeNotification(mStandardLayoutNotifications, pkg, key);
+ }
+
+ public boolean removeNotification(String pkg, String key) {
+ boolean removed = false;
+ removed |= removeImportantNotification(pkg, key);
+ removed |= removeStandardLayoutNotification(pkg, key);
+ return removed;
+ }
+
+ public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
+ String key) {
+ if (map.get(pkg) == null) {
+ map.put(pkg, new ArraySet<>());
+ }
+ map.get(pkg).add(key);
+ }
+
+ public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
+ String pkg, String key) {
+ final boolean found;
+ final ArraySet<String> keys = map.get(pkg);
if (keys == null) {
found = false;
} else {
found = keys.remove(key);
if (keys.size() == 0) {
- mNotifications.remove(pkg);
+ map.remove(pkg);
}
}
return found;
}
+
public boolean isDungeonNeeded() {
if (mRunning != null
&& System.currentTimeMillis() - mServiceStartTime >= FG_SERVICE_GRACE_MILLIS) {
for (String pkg : mRunning) {
- final ArraySet<String> set = mNotifications.get(pkg);
+ final ArraySet<String> set = mImportantNotifications.get(pkg);
if (set == null || set.size() == 0) {
return true;
}
@@ -160,5 +278,27 @@
}
return false;
}
+
+ public ArraySet<Integer> getFeatures(String pkg) {
+ return mAppOps.get(pkg);
+ }
+
+ public String getStandardLayoutKey(String pkg) {
+ final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
+ if (set == null || set.size() == 0) {
+ return null;
+ }
+ return set.valueAt(0);
+ }
+
+ @Override
+ public String toString() {
+ return "UserServices{" +
+ "mRunning=" + Arrays.toString(mRunning) +
+ ", mServiceStartTime=" + mServiceStartTime +
+ ", mImportantNotifications=" + mImportantNotifications +
+ ", mStandardLayoutNotifications=" + mStandardLayoutNotifications +
+ '}';
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 1185f45..3c666e4 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -34,6 +34,8 @@
import android.view.SurfaceControl;
import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.GraphicBufferCompat;
@@ -108,6 +110,15 @@
}
}
+ public void onSplitScreenInvoked() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void setRecentsOnboardingText(CharSequence text) {
mOnboardingText = text;
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index ee573fb..396d317 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -24,6 +24,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
+import java.util.Set;
public final class Prefs {
private Prefs() {} // no instantation
@@ -87,6 +88,7 @@
String NUM_APPS_LAUNCHED = "NumAppsLaunched";
String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
+ String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
@@ -121,6 +123,15 @@
get(context).edit().putString(key, value).apply();
}
+ public static void putStringSet(Context context, @Key String key, Set<String> value) {
+ get(context).edit().putStringSet(key, value).apply();
+ }
+
+ public static Set<String> getStringSet(
+ Context context, @Key String key, Set<String> defaultValue) {
+ return get(context).getStringSet(key, defaultValue);
+ }
+
public static Map<String, ?> getAll(Context context) {
return get(context).getAll();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f3417dc..ea3a60b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,5 +1,10 @@
package com.android.systemui.qs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -8,20 +13,34 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.Scroller;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
import java.util.ArrayList;
+import java.util.Set;
public class PagedTileLayout extends ViewPager implements QSTileLayout {
private static final boolean DEBUG = false;
private static final String TAG = "PagedTileLayout";
+ private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
+ private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
+ private static final long BOUNCE_ANIMATION_DURATION = 450L;
+ private static final int TILE_ANIMATION_STAGGER_DELAY = 85;
+ private static final Interpolator SCROLL_CUBIC = (t) -> {
+ t -= 1.0f;
+ return t * t * t + 1.0f;
+ };
+
private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
@@ -34,37 +53,17 @@
private int mPosition;
private boolean mOffPage;
private boolean mListening;
+ private Scroller mScroller;
+
+ private AnimatorSet mBounceAnimatorSet;
+ private int mAnimatingToPage = -1;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ mScroller = new Scroller(context, SCROLL_CUBIC);
setAdapter(mAdapter);
- setOnPageChangeListener(new OnPageChangeListener() {
- @Override
- public void onPageSelected(int position) {
- if (mPageIndicator == null) return;
- if (mPageListener != null) {
- mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
- : position == 0);
- }
- }
-
- @Override
- 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 &&
- (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
- setCurrentItem(0);
+ setOnPageChangeListener(mOnPageChangeListener);
+ setCurrentItem(0, false);
}
@Override
@@ -99,6 +98,45 @@
}
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Suppress all touch event during reveal animation.
+ if (mAnimatingToPage != -1) {
+ return true;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Suppress all touch event during reveal animation.
+ if (mAnimatingToPage != -1) {
+ return true;
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
+ public void computeScroll() {
+ if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
+ scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+ float pageFraction = (float) getScrollX() / getWidth();
+ int position = (int) pageFraction;
+ float positionOffset = pageFraction - position;
+ mOnPageChangeListener.onPageScrolled(position, positionOffset, getScrollX());
+ // Keep on drawing until the animation has finished.
+ postInvalidateOnAnimation();
+ return;
+ }
+ if (mAnimatingToPage != -1) {
+ setCurrentItem(mAnimatingToPage, true);
+ mBounceAnimatorSet.start();
+ setOffscreenPageLimit(1);
+ mAnimatingToPage = -1;
+ }
+ 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
@@ -257,9 +295,84 @@
return mPages.get(0).mColumns;
}
+ public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
+ if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0) {
+ // Do not start the reveal animation unless there are tiles to animate, multiple
+ // TilePages available and the user has not already started dragging.
+ return;
+ }
+
+ final int lastPageNumber = mPages.size() - 1;
+ final TilePage lastPage = mPages.get(lastPageNumber);
+ final ArrayList<Animator> bounceAnims = new ArrayList<>();
+ for (TileRecord tr : lastPage.mRecords) {
+ if (tileSpecs.contains(tr.tile.getTileSpec())) {
+ bounceAnims.add(setupBounceAnimator(tr.tileView, bounceAnims.size()));
+ }
+ }
+
+ if (bounceAnims.isEmpty()) {
+ // All tileSpecs are on the first page. Nothing to do.
+ // TODO: potentially show a bounce animation for first page QS tiles
+ return;
+ }
+
+ mBounceAnimatorSet = new AnimatorSet();
+ mBounceAnimatorSet.playTogether(bounceAnims);
+ mBounceAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBounceAnimatorSet = null;
+ postAnimation.run();
+ }
+ });
+ mAnimatingToPage = lastPageNumber;
+ setOffscreenPageLimit(mAnimatingToPage); // Ensure the page to reveal has been inflated.
+ mScroller.startScroll(getScrollX(), getScrollY(), getWidth() * mAnimatingToPage, 0,
+ REVEAL_SCROLL_DURATION_MILLIS);
+ postInvalidateOnAnimation();
+ }
+
+ private static Animator setupBounceAnimator(View view, int ordinal) {
+ view.setAlpha(0f);
+ view.setScaleX(0f);
+ view.setScaleY(0f);
+ ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1),
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 1),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 1));
+ animator.setDuration(BOUNCE_ANIMATION_DURATION);
+ animator.setStartDelay(ordinal * TILE_ANIMATION_STAGGER_DELAY);
+ animator.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
+ return animator;
+ }
+
+ private final ViewPager.OnPageChangeListener mOnPageChangeListener =
+ new ViewPager.SimpleOnPageChangeListener() {
+ @Override
+ public void onPageSelected(int position) {
+ if (mPageIndicator == null) return;
+ if (mPageListener != null) {
+ mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
+ : position == 0);
+ }
+ }
+
+ @Override
+ 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 &&
+ (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
+ }
+ }
+ };
+
public static class TilePage extends TileLayout {
private int mMaxRows = 3;
-
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
updateResources();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 993df759..7b48e02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -169,10 +169,10 @@
private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
.addFloat(mDivider, "alpha", 0, 1)
- .addFloat(mCarrierText, "alpha", 0, 1)
+ .addFloat(mCarrierText, "alpha", 0, 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mDragHandle, "translationY", mDragHandleExpandOffset, 0)
- .addFloat(mDragHandle, "alpha", 1, 0)
+ .addFloat(mDragHandle, "alpha", 1, 0, 0)
.setStartDelay(0.15f)
.build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 5758762..29f3c43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -290,6 +290,7 @@
// Let the views animate their contents correctly by giving them the necessary context.
mHeader.setExpansion(mKeyguardShowing, expansion, panelTranslationY);
mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
+ mQSPanel.getQsTileRevealController().setExpansion(expansion);
mQSPanel.setTranslationY(translationScaleY * heightDiff);
mQSDetail.setFullyExpanded(fullyExpanded);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 143ad21..61e3065 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -60,11 +60,12 @@
public static final String QS_SHOW_HEADER = "qs_show_header";
protected final Context mContext;
- protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
+ protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected final View mBrightnessView;
private final H mHandler = new H();
private final View mPageIndicator;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final QSTileRevealController mQsTileRevealController;
protected boolean mExpanded;
protected boolean mListening;
@@ -108,6 +109,8 @@
addView(mPageIndicator);
((PagedTileLayout) mTileLayout).setPageIndicator((PageIndicator) mPageIndicator);
+ mQsTileRevealController = new QSTileRevealController(mContext, this,
+ ((PagedTileLayout) mTileLayout));
addDivider();
@@ -136,6 +139,10 @@
return mPageIndicator;
}
+ public QSTileRevealController getQsTileRevealController() {
+ return mQsTileRevealController;
+ }
+
public boolean isShowingCustomize() {
return mCustomizePanel != null && mCustomizePanel.isCustomizing();
}
@@ -352,6 +359,9 @@
}
public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
+ if (!collapsedView) {
+ mQsTileRevealController.updateRevealedTiles(tiles);
+ }
for (TileRecord record : mRecords) {
mTileLayout.removeTile(record);
record.tile.removeCallback(record.callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
new file mode 100644
index 0000000..2f012e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
@@ -0,0 +1,76 @@
+package com.android.systemui.qs;
+
+import static com.android.systemui.Prefs.Key.QS_TILE_SPECS_REVEALED;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.ArraySet;
+
+import com.android.systemui.Prefs;
+import com.android.systemui.plugins.qs.QSTile;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+public class QSTileRevealController {
+ private static final long QS_REVEAL_TILES_DELAY = 500L;
+
+ private final Context mContext;
+ private final QSPanel mQSPanel;
+ private final PagedTileLayout mPagedTileLayout;
+ private final ArraySet<String> mTilesToReveal = new ArraySet<>();
+ private final Handler mHandler = new Handler();
+
+ private final Runnable mRevealQsTiles = new Runnable() {
+ @Override
+ public void run() {
+ mPagedTileLayout.startTileReveal(mTilesToReveal, () -> {
+ if (mQSPanel.isExpanded()) {
+ addTileSpecsToRevealed(mTilesToReveal);
+ mTilesToReveal.clear();
+ }
+ });
+ }
+ };
+
+ QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout) {
+ mContext = context;
+ mQSPanel = qsPanel;
+ mPagedTileLayout = pagedTileLayout;
+ }
+
+ public void setExpansion(float expansion) {
+ if (expansion == 1f) {
+ mHandler.postDelayed(mRevealQsTiles, QS_REVEAL_TILES_DELAY);
+ } else {
+ mHandler.removeCallbacks(mRevealQsTiles);
+ }
+ }
+
+ public void updateRevealedTiles(Collection<QSTile> tiles) {
+ ArraySet<String> tileSpecs = new ArraySet<>();
+ for (QSTile tile : tiles) {
+ tileSpecs.add(tile.getTileSpec());
+ }
+
+ final Set<String> revealedTiles = Prefs.getStringSet(
+ mContext, QS_TILE_SPECS_REVEALED, Collections.EMPTY_SET);
+ if (revealedTiles.isEmpty() || mQSPanel.isShowingCustomize()) {
+ // Do not reveal QS tiles the user has upon first load or those that they directly
+ // added through customization.
+ addTileSpecsToRevealed(tileSpecs);
+ } else {
+ // Animate all tiles that the user has not directly added themselves.
+ tileSpecs.removeAll(revealedTiles);
+ mTilesToReveal.addAll(tileSpecs);
+ }
+ }
+
+ private void addTileSpecsToRevealed(ArraySet<String> specs) {
+ final ArraySet<String> revealedTiles = new ArraySet<>(
+ Prefs.getStringSet(mContext, QS_TILE_SPECS_REVEALED, Collections.EMPTY_SET));
+ revealedTiles.addAll(specs);
+ Prefs.putStringSet(mContext, QS_TILE_SPECS_REVEALED, revealedTiles);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 23faa55..66823ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -21,7 +21,8 @@
protected int mColumns;
protected int mCellWidth;
protected int mCellHeight;
- protected int mCellMargin;
+ protected int mCellMarginHorizontal;
+ protected int mCellMarginVertical;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mCellMarginTop;
@@ -76,7 +77,8 @@
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
- mCellMargin = res.getDimensionPixelSize(R.dimen.qs_tile_margin);
+ mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
+ mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
if (mColumns != columns) {
mColumns = columns;
@@ -91,7 +93,7 @@
final int numTiles = mRecords.size();
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int rows = (numTiles + mColumns - 1) / mColumns;
- mCellWidth = (width - (mCellMargin * (mColumns + 1))) / mColumns;
+ mCellWidth = (width - (mCellMarginHorizontal * (mColumns + 1))) / mColumns;
View previousView = this;
for (TileRecord record : mRecords) {
@@ -102,8 +104,8 @@
// Only include the top margin in our measurement if we have more than 1 row to show.
// Otherwise, don't add the extra margin buffer at top.
- int height = (mCellHeight + mCellMargin) * rows +
- (rows != 0 ? (mCellMarginTop - mCellMargin) : 0);
+ int height = (mCellHeight + mCellMarginVertical) * rows +
+ (rows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
if (height < 0) height = 0;
setMeasuredDimension(width, height);
}
@@ -143,10 +145,10 @@
}
private int getRowTop(int row) {
- return row * (mCellHeight + mCellMargin) + mCellMarginTop;
+ return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
}
private int getColumnStart(int column) {
- return column * (mCellWidth + mCellMargin) + mCellMargin;
+ return column * (mCellWidth + mCellMarginHorizontal) + mCellMarginHorizontal;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 6263efa..f673364 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -200,7 +200,6 @@
}
private static abstract class KeyframeSet {
-
private final float mFrameWidth;
private final int mSize;
@@ -210,9 +209,8 @@
}
void setValue(float fraction, Object target) {
- int i;
- for (i = 1; i < mSize - 1 && fraction > mFrameWidth; i++);
- float amount = fraction / mFrameWidth;
+ int i = MathUtils.constrain((int) Math.ceil(fraction / mFrameWidth), 1, mSize - 1);
+ float amount = (fraction - mFrameWidth * (i - 1)) / mFrameWidth;
interpolate(i, amount, target);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 409c753..df4a975 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -45,8 +45,10 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
@@ -100,6 +102,8 @@
private static RecentsTaskLoader sTaskLoader;
private static RecentsConfiguration sConfiguration;
+ private OverviewProxyService mOverviewProxyService;
+
private Handler mHandler;
private RecentsImpl mImpl;
private int mDraggingInRecentsCurrentUser;
@@ -208,6 +212,7 @@
sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
mHandler = new Handler();
mImpl = new RecentsImpl(mContext);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
// Register with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -247,6 +252,13 @@
return;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // TODO: Proxy to Launcher
+ if (!triggeredFromAltTab) {
+ return;
+ }
+ }
+
ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
int currentUser = sSystemServicesProxy.getCurrentUser();
@@ -282,6 +294,13 @@
return;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // TODO: Proxy to Launcher
+ if (!triggeredFromAltTab) {
+ return;
+ }
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
@@ -313,6 +332,11 @@
return;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // TODO: Proxy to Launcher
+ return;
+ }
+
int growTarget = getComponent(Divider.class).getView().growsRecents();
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
@@ -345,6 +369,11 @@
return;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // TODO: Proxy to Launcher
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.preloadRecents();
@@ -373,6 +402,11 @@
return;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // TODO: Proxy to Launcher
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.cancelPreloadingRecents();
@@ -415,7 +449,7 @@
final int activityType = runningTask != null
? runningTask.configuration.windowConfiguration.getActivityType()
: ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive();
+ boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
boolean isRunningTaskInHomeOrRecentsStack =
activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b0a2fad..95b311f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -96,6 +96,7 @@
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -836,12 +837,7 @@
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
// We post to make sure that this information is delivered after this traversals is
// finished.
- mRecentsView.post(new Runnable() {
- @Override
- public void run() {
- Recents.getSystemServices().endProlongedAnimations();
- }
- });
+ mRecentsView.post(() -> WindowManagerWrapper.getInstance().endProlongedAnimations());
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 3f6f30b..055e72e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,7 +24,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.KeyguardManager;
import android.app.trust.TrustManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -34,7 +33,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask.Status;
import android.os.Handler;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -385,8 +383,7 @@
}
public void toggleRecents(int growTarget) {
- // Skip preloading if the task is locked
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return;
}
@@ -464,8 +461,7 @@
}
public void preloadRecents() {
- // Skip preloading if the task is locked
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 26fac6c..127361a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -49,6 +49,10 @@
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Shows onboarding for the new recents interaction in P (codenamed quickstep).
*/
@@ -65,6 +69,7 @@
private final Context mContext;
private final WindowManager mWindowManager;
private final OverviewProxyService mOverviewProxyService;
+ private Set<String> mBlacklistedPackages;
private final View mLayout;
private final TextView mTextView;
private final ImageView mDismissView;
@@ -85,6 +90,10 @@
public void onTaskStackChanged() {
ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance()
.getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */);
+ if (mBlacklistedPackages.contains(info.baseActivity.getPackageName())) {
+ hide(true);
+ return;
+ }
int activityType = info.configuration.windowConfiguration.getActivityType();
int numAppsLaunched = Prefs.getInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
if (activityType == ACTIVITY_TYPE_STANDARD) {
@@ -122,6 +131,9 @@
mOverviewProxyService = overviewProxyService;
final Resources res = context.getResources();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mBlacklistedPackages = new HashSet<>();
+ Collections.addAll(mBlacklistedPackages, res.getStringArray(
+ R.array.recents_onboarding_blacklisted_packages));
mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
mTextView = mLayout.findViewById(R.id.onboarding_text);
mDismissView = mLayout.findViewById(R.id.dismiss);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 93fd34a..544d95c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -444,17 +444,6 @@
}
}
- public void endProlongedAnimations() {
- if (mWm == null) {
- return;
- }
- try {
- mIwm.endProlongedAnimations();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
public void registerDockedStackListener(IDockedStackListener listener) {
if (mWm == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 3cc3273..89288d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -2188,7 +2188,8 @@
private void readSystemFlags() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled();
+ mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isScreenPinningEnabled()
+ && !ActivityManagerWrapper.getInstance().isLockToAppActive();
}
private void updateStackActionButtonVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
new file mode 100644
index 0000000..2ec78cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+
+/**
+ * This class handles listening to notification updates and passing them along to
+ * NotificationPresenter to be displayed to the user.
+ */
+public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener {
+ private static final String TAG = "NotificationListener";
+
+ // Dependencies:
+ private final ForegroundServiceController mFsc =
+ Dependency.get(ForegroundServiceController.class);
+
+ private final Context mContext;
+ protected NotificationPresenter mPresenter;
+ protected NotificationEntryManager mEntryManager;
+ protected final AppOpsManager mAppOps;
+
+ protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ AppOpsManager.OP_RECORD_AUDIO};
+
+ public AppOpsListener(Context context) {
+ mContext = context;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ }
+
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationEntryManager entryManager) {
+ mPresenter = presenter;
+ mEntryManager = entryManager;
+ mAppOps.startWatchingActive(OPS, this);
+ }
+
+ public void destroy() {
+ mAppOps.stopWatchingActive(this);
+ }
+
+ @Override
+ public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
+ mFsc.onAppOpChanged(code, uid, packageName, active);
+ mPresenter.getHandler().post(() -> {
+ mEntryManager.updateNotificationsForAppOps(code, uid, packageName, active);
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index bc2dff9..785fc1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.MathUtils;
@@ -1354,6 +1355,14 @@
mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
}
+ public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+ if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
+ mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
+ }
+ mPrivateLayout.showAppOpsIcons(activeOps);
+ mPublicLayout.showAppOpsIcons(activeOps);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -2629,6 +2638,16 @@
mChildrenContainer = childrenContainer;
}
+ @VisibleForTesting
+ protected void setPrivateLayout(NotificationContentView privateLayout) {
+ mPrivateLayout = privateLayout;
+ }
+
+ @VisibleForTesting
+ protected void setPublicLayout(NotificationContentView publicLayout) {
+ mPublicLayout = publicLayout;
+ }
+
/**
* Equivalent to View.OnLongClickListener with coordinates
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 91960df..73c8795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.view.NotificationHeaderView;
@@ -1423,6 +1424,17 @@
return header;
}
+ public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+ if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
+ mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
+ mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
+ mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+ }
+ }
public NotificationHeaderView getContractedNotificationHeader() {
if (mContractedChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 127f3f9..d53cb03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -34,6 +35,7 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -65,6 +67,8 @@
private final Environment mEnvironment;
private HeadsUpManager mHeadsUpManager;
+ final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class);
+
public static final class Entry {
private static final long LAUNCH_COOLDOWN = 2000;
private static final long REMOTE_INPUT_COOLDOWN = 500;
@@ -95,6 +99,7 @@
private Throwable mDebugThrowable;
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+ public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
public Entry(StatusBarNotification n) {
this.key = n.getKey();
@@ -194,7 +199,7 @@
/**
* Update the notification icons.
* @param context the context to create the icons with.
- * @param n the notification to read the icon from.
+ * @param sbn the notification to read the icon from.
* @throws InflationException
*/
public void updateIcons(Context context, StatusBarNotification sbn)
@@ -375,6 +380,8 @@
}
mGroupManager.onEntryAdded(entry);
+ updateAppOps(entry);
+
updateRankingAndSort(mRankingMap);
}
@@ -393,6 +400,35 @@
updateRankingAndSort(ranking);
}
+ private void updateAppOps(Entry entry) {
+ final int uid = entry.notification.getUid();
+ final String pkg = entry.notification.getPackageName();
+ ArraySet<Integer> activeOps = mFsc.getAppOps(entry.notification.getUserId(), pkg);
+ if (activeOps != null) {
+ int N = activeOps.size();
+ for (int i = 0; i < N; i++) {
+ updateAppOp(activeOps.valueAt(i), uid, pkg, true);
+ }
+ }
+ }
+
+ public void updateAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+ synchronized (mEntries) {
+ final int N = mEntries.size();
+ for (int i = 0; i < N; i++) {
+ Entry entry = mEntries.valueAt(i);
+ if (uid == entry.notification.getUid()
+ && pkg.equals(entry.notification.getPackageName())) {
+ if (showIcon) {
+ entry.mActiveAppOps.add(appOp);
+ } else {
+ entry.mActiveAppOps.remove(appOp);
+ }
+ }
+ }
+ }
+ }
+
public boolean isAmbient(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
@@ -545,11 +581,14 @@
return true;
}
- final ForegroundServiceController fsc = Dependency.get(ForegroundServiceController.class);
- if (fsc.isDungeonNotification(sbn) && !fsc.isDungeonNeededForUser(sbn.getUserId())) {
+ if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) {
// this is a foreground-service disclosure for a user that does not need to show one
return true;
}
+ if (mFsc.isSystemAlertNotification(sbn) && !mFsc.isSystemAlertWarningNeeded(
+ sbn.getUserId(), sbn.getPackageName())) {
+ return true;
+ }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7360486..71f7911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
@@ -77,7 +78,7 @@
public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
VisualStabilityManager.Callback {
- private static final String TAG = "NotificationEntryManager";
+ private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = false;
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
@@ -734,6 +735,14 @@
}
}
+ public void updateNotificationsForAppOps(int appOp, int uid, String pkg, boolean showIcon) {
+ if (mForegroundServiceController.getStandardLayoutKey(
+ UserHandle.getUserId(uid), pkg) != null) {
+ mNotificationData.updateAppOp(appOp, uid, pkg, showIcon);
+ updateNotifications();
+ }
+ }
+
private boolean alertAgain(NotificationData.Entry oldEntry, Notification newNotification) {
return oldEntry == null || !oldEntry.hasInterrupted()
|| (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index cd4c7ae..75b8b37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -342,6 +342,8 @@
row.showBlockingHelper(entry.userSentiment ==
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
+ row.showAppOpsIcons(entry.mActiveAppOps);
}
mPresenter.onUpdateRowStates();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aba5cdf..d2cdc27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.v4.util.ArraySet;
import android.util.Log;
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -45,12 +47,12 @@
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener {
+ OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
private static final String TAG = "HeadsUpManagerPhone";
private static final boolean DEBUG = false;
private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
+ private int mStatusBarHeight;
private final NotificationGroupManager mGroupManager;
private final StatusBar mBar;
private final VisualStabilityManager mVisualStabilityManager;
@@ -291,6 +293,13 @@
}
}
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ Resources resources = mContext.getResources();
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// VisualStabilityManager.Callback overrides:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 380c08e..edfbd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -21,6 +21,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
+import android.util.StatsLog;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -152,6 +153,8 @@
mKeyguardView.requestLayout();
}
mShowingSoon = false;
+ StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
+ StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
}
};
@@ -183,6 +186,8 @@
public void hide(boolean destroyView) {
if (isShowing()) {
+ StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
+ StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
mDismissCallbackRegistry.notifyDismissCancelled();
}
mFalsingManager.onBouncerHidden();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 62151cf..0ed69e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -708,7 +708,8 @@
@VisibleForTesting
boolean onHomeLongClick(View v) {
- if (!mNavigationBarView.isRecentsButtonVisible() && mNavigationBarView.inScreenPinning()) {
+ if (!mNavigationBarView.isRecentsButtonVisible()
+ && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return onLongPressBackHome(v);
}
if (shouldDisableNavbarGestures()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 320b56f..a4daed9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -149,7 +150,8 @@
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()
+ || mStatusBar.isKeyguardShowing()) {
return false;
}
@@ -182,7 +184,8 @@
}
public boolean onTouchEvent(MotionEvent event) {
- if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing()) {
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()
+ || mStatusBar.isKeyguardShowing()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index a5621e5..74fbed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -21,8 +21,6 @@
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
@@ -30,7 +28,6 @@
import android.animation.ValueAnimator;
import android.annotation.DrawableRes;
import android.annotation.StyleRes;
-import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -41,7 +38,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
@@ -60,7 +56,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
-import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
@@ -379,15 +374,20 @@
return getRecentsButton().getVisibility() == View.VISIBLE;
}
+ public boolean isOverviewEnabled() {
+ return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0;
+ }
+
public boolean isQuickStepSwipeUpEnabled() {
return mOverviewProxyService.getProxy() != null
+ && isOverviewEnabled()
&& ((mOverviewProxyService.getInteractionFlags()
& FLAG_DISABLE_SWIPE_UP) == 0);
}
public boolean isQuickScrubEnabled() {
return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
- && mOverviewProxyService.getProxy() != null && !isRecentsButtonVisible()
+ && mOverviewProxyService.getProxy() != null && isOverviewEnabled()
&& ((mOverviewProxyService.getInteractionFlags()
& FLAG_DISABLE_QUICK_SCRUB) == 0);
}
@@ -575,8 +575,7 @@
boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
// Always disable recents when alternate car mode UI is active.
- boolean disableRecent = mUseCarModeUi
- || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
+ boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
&& ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
@@ -584,17 +583,18 @@
// When screen pinning, don't hide back and home when connected service or back and
// recents buttons when disconnected from launcher service in screen pinning mode,
// as they are used for exiting.
+ final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
if (mOverviewProxyService.getProxy() != null) {
// Use interaction flags to show/hide navigation buttons but will be shown if required
// to exit screen pinning.
final int flags = mOverviewProxyService.getInteractionFlags();
disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
- if (inScreenPinning()) {
+ if (pinningActive) {
disableBack = disableHome = false;
} else {
disableBack |= (flags & FLAG_HIDE_BACK_BUTTON) != 0;
}
- } else if (inScreenPinning()) {
+ } else if (pinningActive) {
disableBack = disableRecent = false;
}
@@ -614,7 +614,7 @@
}
public boolean inScreenPinning() {
- return ActivityManagerWrapper.getInstance().isLockToAppActive();
+ return ActivityManagerWrapper.getInstance().isScreenPinningActive();
}
public void setLayoutTransitionsEnabled(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index cc5a93c..900ec0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -264,7 +264,7 @@
mScrimController.setPanelExpansion(scrimFraction);
}
- public void onDensityOrFontScaleChanged() {
+ public void updateResources() {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = getResources().getDimensionPixelSize(
R.dimen.status_bar_height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 933c952..86e618e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -180,6 +180,7 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -210,7 +211,6 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -240,7 +240,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -407,6 +406,7 @@
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
protected NotificationViewHierarchyManager mViewHierarchyManager;
+ protected AppOpsListener mAppOpsListener;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -624,6 +624,8 @@
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
+ mAppOpsListener = Dependency.get(AppOpsListener.class);
+ mAppOpsListener.setUpWithPresenter(this, mEntryManager);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
@@ -815,6 +817,7 @@
mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
mVisualStabilityManager);
+ Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
@@ -1071,7 +1074,6 @@
// end old BaseStatusBar.onDensityOrFontScaleChanged().
mScrimController.onDensityOrFontScaleChanged();
// TODO: Remove this.
- if (mStatusBarView != null) mStatusBarView.onDensityOrFontScaleChanged();
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
}
@@ -3081,6 +3083,9 @@
loadDimens();
+ if (mStatusBarView != null) {
+ mStatusBarView.updateResources();
+ }
if (mNotificationPanel != null) {
mNotificationPanel.updateResources();
}
@@ -3295,6 +3300,7 @@
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
mDeviceProvisionedController.removeCallback(mUserSetupObserver);
Dependency.get(ConfigurationController.class).removeCallback(this);
+ mAppOpsListener.destroy();
}
private boolean mDemoModeAllowed;
@@ -4521,7 +4527,7 @@
if (isScreenTurningOnOrOn()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.hideBouncer(false /* destroyView */);
+ mStatusBarKeyguardViewManager.reset(true /* hide */);
}
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
updateScrimController();
@@ -4727,7 +4733,7 @@
@Override
public boolean isPowerSaveActive() {
- return mBatteryController.isPowerSave();
+ return mBatteryController.isAodPowerSave();
}
@Override
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 47ea3a7..a009d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
+import android.util.StatsLog;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -140,6 +141,8 @@
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true);
reset(true /* hideBouncerWhenShowing */);
+ StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
+ StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
/**
@@ -161,7 +164,7 @@
updateStates();
}
- public void hideBouncer(boolean destroyView) {
+ private void hideBouncer(boolean destroyView) {
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
@@ -289,6 +292,8 @@
public void setOccluded(boolean occluded, boolean animate) {
mStatusBar.setOccluded(occluded);
if (occluded && !mOccluded && mShowing) {
+ StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
+ StatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
if (mStatusBar.isInLaunchTransition()) {
mOccluded = true;
mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
@@ -301,6 +306,9 @@
});
return;
}
+ } else if (!occluded && mOccluded && mShowing) {
+ StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
+ StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
boolean isOccluding = !mOccluded && occluded;
mOccluded = occluded;
@@ -398,6 +406,8 @@
mStatusBarWindowManager.setKeyguardShowing(false);
mViewMediatorCallback.keyguardGone();
}
+ StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
+ StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
}
public void onDensityOrFontScaleChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 641fe69..6f4026d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -41,6 +41,13 @@
boolean isPowerSave();
/**
+ * Returns {@code true} if AOD was disabled by power saving policies.
+ */
+ default boolean isAodPowerSave() {
+ return isPowerSave();
+ }
+
+ /**
* A listener that will be notified whenever a change in battery level or power save mode
* has occurred.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index e8d5af6..49f880c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -24,8 +24,10 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.PowerSaveState;
import android.util.Log;
-import com.android.systemui.DemoMode;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -52,13 +54,19 @@
protected boolean mCharging;
protected boolean mCharged;
protected boolean mPowerSave;
+ protected boolean mAodPowerSave;
private boolean mTestmode = false;
private boolean mHasReceivedBattery = false;
public BatteryControllerImpl(Context context) {
+ this(context, context.getSystemService(PowerManager.class));
+ }
+
+ @VisibleForTesting
+ BatteryControllerImpl(Context context, PowerManager powerManager) {
mContext = context;
mHandler = new Handler();
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = powerManager;
registerReceiver();
updatePowerSave();
@@ -166,6 +174,11 @@
return mPowerSave;
}
+ @Override
+ public boolean isAodPowerSave() {
+ return mAodPowerSave;
+ }
+
private void updatePowerSave() {
setPowerSave(mPowerManager.isPowerSaveMode());
}
@@ -173,6 +186,11 @@
private void setPowerSave(boolean powerSave) {
if (powerSave == mPowerSave) return;
mPowerSave = powerSave;
+
+ // AOD power saving setting might be different from PowerManager power saving mode.
+ PowerSaveState state = mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD);
+ mAodPowerSave = state.batterySaverEnabled;
+
if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
firePowerSaveChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1b55a5b..66fde79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1394,6 +1394,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = getResources().getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 943020c..18dd3c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -16,6 +16,14 @@
package com.android.systemui;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationManager;
@@ -24,17 +32,14 @@
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
import com.android.internal.messages.nano.SystemMessageProto;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForegroundServiceControllerTest extends SysuiTestCase {
@@ -49,7 +54,7 @@
}
@Test
- public void testNotificationCRUD() {
+ public void testNotificationCRUD_dungeon() {
StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, "com.example.app1");
StatusBarNotification sbn_user2_app2_fg = makeMockFgSBN(USERID_TWO, "com.example.app2");
StatusBarNotification sbn_user1_app3_fg = makeMockFgSBN(USERID_ONE, "com.example.app3");
@@ -98,6 +103,101 @@
}
@Test
+ public void testNotificationCRUD_stdLayout() {
+ StatusBarNotification sbn_user1_app1_fg =
+ makeMockFgSBN(USERID_ONE, "com.example.app1", 0, true);
+ StatusBarNotification sbn_user2_app2_fg =
+ makeMockFgSBN(USERID_TWO, "com.example.app2", 1, true);
+ StatusBarNotification sbn_user1_app3_fg =
+ makeMockFgSBN(USERID_ONE, "com.example.app3", 2, true);
+ StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
+ 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
+ StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
+ 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
+
+ assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
+ assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
+
+ // these are never added to the tracker
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
+ // should still not be there
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+
+ fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
+ fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+
+ assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
+
+ assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
+ assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
+
+ assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
+ assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
+
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.removeNotification(sbn_user2_app1));
+ }
+
+ @Test
+ public void testAppOpsCRUD() {
+ // no crash on remove that doesn't exist
+ fsc.onAppOpChanged(9, 1000, "pkg1", false);
+ assertNull(fsc.getAppOps(0, "pkg1"));
+
+ // multiuser & multipackage
+ fsc.onAppOpChanged(8, 50, "pkg1", true);
+ fsc.onAppOpChanged(1, 60, "pkg3", true);
+ fsc.onAppOpChanged(7, 500000, "pkg2", true);
+
+ assertEquals(1, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+
+ assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+ assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+
+ assertEquals(1, fsc.getAppOps(0, "pkg3").size());
+ assertTrue(fsc.getAppOps(0, "pkg3").contains(1));
+
+ // multiple ops for the same package
+ fsc.onAppOpChanged(9, 50, "pkg1", true);
+ fsc.onAppOpChanged(5, 50, "pkg1", true);
+
+ assertEquals(3, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(9));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+
+ assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+ assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+
+ // remove one of the multiples
+ fsc.onAppOpChanged(9, 50, "pkg1", false);
+ assertEquals(2, fsc.getAppOps(0, "pkg1").size());
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+ assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+
+ // remove last op
+ fsc.onAppOpChanged(1, 60, "pkg3", false);
+ assertNull(fsc.getAppOps(0, "pkg3"));
+ }
+
+ @Test
public void testDungeonPredicate() {
StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
@@ -252,6 +352,14 @@
assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+ // importance upgrade
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+ assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+ sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1_fg,
+ NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
+
// finally, let's turn off the service
fsc.addNotification(makeMockDungeon(USERID_ONE, null),
NotificationManager.IMPORTANCE_DEFAULT);
@@ -260,12 +368,71 @@
assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
}
+ @Test
+ public void testStdLayoutBasic() {
+ final String PKG1 = "com.example.app0";
+
+ StatusBarNotification sbn_user1_app1 = makeMockFgSBN(USERID_ONE, PKG1, 0, true);
+ sbn_user1_app1.getNotification().flags = 0;
+ StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
+ // let's take out the non-fg notification and see what happens.
+ fsc.removeNotification(sbn_user1_app1);
+ // still covered by sbn_user1_app1_fg
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
+
+ // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
+ StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
+ sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
+ fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+ // ok, ok, we'll put it back
+ sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
+
+ assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
+
+ // let's try a custom layout
+ sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, false);
+ fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+ // now let's test an upgrade (non fg to fg)
+ fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
+ sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ fsc.updateNotification(sbn_user1_app1,
+ NotificationManager.IMPORTANCE_MIN); // this is now a fg notification
+
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+
+ // remove it, make sure we're out of compliance again
+ assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
+ assertFalse(fsc.removeNotification(sbn_user1_app1));
+ assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+ assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+ }
+
private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
int flags) {
final Notification n = mock(Notification.class);
+ n.extras = new Bundle();
n.flags = flags;
return makeMockSBN(userid, pkg, id, tag, n);
}
+
private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
Notification n) {
final StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -278,9 +445,25 @@
when(sbn.getKey()).thenReturn("MOCK:"+userid+"|"+pkg+"|"+id+"|"+tag);
return sbn;
}
+
+ private StatusBarNotification makeMockFgSBN(int userid, String pkg, int id,
+ boolean usesStdLayout) {
+ StatusBarNotification sbn =
+ makeMockSBN(userid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE);
+ if (usesStdLayout) {
+ sbn.getNotification().contentView = null;
+ sbn.getNotification().headsUpContentView = null;
+ sbn.getNotification().bigContentView = null;
+ } else {
+ sbn.getNotification().contentView = mock(RemoteViews.class);
+ }
+ return sbn;
+ }
+
private StatusBarNotification makeMockFgSBN(int userid, String pkg) {
return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
}
+
private StatusBarNotification makeMockDungeon(int userid, String[] pkgs) {
final Notification n = mock(Notification.class);
n.flags = Notification.FLAG_ONGOING_EVENT;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 11491a75..2040e75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -54,7 +54,7 @@
// Layout needs to leave space for the tile margins. Three times the margin size is
// sufficient for any number of columns.
mLayoutSizeForOneTile =
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_margin) * 3;
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) * 3;
}
private QSPanel.TileRecord createTileRecord() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java
index 641cdc7..4cc0e20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java
@@ -56,6 +56,28 @@
}
@Test
+ public void testSetValueFloat_threeValues() {
+ TouchAnimator animator = new TouchAnimator.Builder()
+ .addFloat(mTestView, "x", 0, 20, 50)
+ .build();
+
+ animator.setPosition(0);
+ assertEquals(0f, mTestView.getX());
+
+ animator.setPosition(.25f);
+ assertEquals(10f, mTestView.getX());
+
+ animator.setPosition(.5f);
+ assertEquals(20f, mTestView.getX());
+
+ animator.setPosition(.75f);
+ assertEquals(35f, mTestView.getX());
+
+ animator.setPosition(1);
+ assertEquals(50f, mTestView.getX());
+ }
+
+ @Test
public void testSetValueInt() {
TouchAnimator animator = new TouchAnimator.Builder()
.addInt(mTestView, "top", 0, 50)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
new file mode 100644
index 0000000..2a48c4b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AppOpsListenerTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private AppOpsManager mAppOpsManager;
+
+ // Dependency mocks:
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private ForegroundServiceController mFsc;
+
+ private AppOpsListener mListener;
+ private Handler mHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
+ mHandler = new Handler(Looper.getMainLooper());
+ when(mPresenter.getHandler()).thenReturn(mHandler);
+
+ mListener = new AppOpsListener(mContext);
+ }
+
+ @Test
+ public void testOnlyListenForFewOps() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+
+ verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
+ }
+
+ @Test
+ public void testStopListening() {
+ mListener.destroy();
+ verify(mAppOpsManager, times(1)).stopWatchingActive(mListener);
+ }
+
+ @Test
+ public void testInformEntryMgrOnAppOpsChange() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+ mListener.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ waitForIdleSync(mHandler);
+ verify(mEntryManager, times(1)).updateNotificationsForAppOps(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void testInformFscOnAppOpsChange() {
+ mListener.setUpWithPresenter(mPresenter, mEntryManager);
+ mListener.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ waitForIdleSync(mHandler);
+ verify(mFsc, times(1)).onAppOpChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 544585a..ce629bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -19,10 +19,15 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -146,4 +151,34 @@
Assert.assertTrue("Should always play sounds when not trusted.",
mGroup.isSoundEffectsEnabled());
}
+
+ @Test
+ public void testShowAppOpsIcons_noHeader() {
+ // public notification is custom layout - no header
+ mGroup.setSensitive(true, true);
+ mGroup.showAppOpsIcons(new ArraySet<>());
+ }
+
+ @Test
+ public void testShowAppOpsIcons_header() throws Exception {
+ NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
+
+ NotificationContentView publicLayout = mock(NotificationContentView.class);
+ mGroup.setPublicLayout(publicLayout);
+ NotificationContentView privateLayout = mock(NotificationContentView.class);
+ mGroup.setPrivateLayout(privateLayout);
+ NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+ when(mockContainer.getNotificationChildCount()).thenReturn(1);
+ when(mockContainer.getHeaderView()).thenReturn(mockHeader);
+ mGroup.setChildrenContainer(mockContainer);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+ mGroup.showAppOpsIcons(ops);
+
+ verify(mockHeader, times(1)).showAppOpsIcons(ops);
+ verify(privateLayout, times(1)).showAppOpsIcons(ops);
+ verify(publicLayout, times(1)).showAppOpsIcons(ops);
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
index 436849c..1fb4c37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -16,14 +16,23 @@
package com.android.systemui.statusbar;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -75,4 +84,35 @@
mView.setHeadsUpAnimatingAway(true);
Assert.assertFalse(mView.isAnimatingVisibleType());
}
+
+ @Test
+ @UiThreadTest
+ public void testShowAppOpsIcons() {
+ NotificationHeaderView mockContracted = mock(NotificationHeaderView.class);
+ when(mockContracted.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockContracted);
+ NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class);
+ when(mockExpanded.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockExpanded);
+ NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class);
+ when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockHeadsUp);
+ NotificationHeaderView mockAmbient = mock(NotificationHeaderView.class);
+ when(mockAmbient.findViewById(com.android.internal.R.id.notification_header))
+ .thenReturn(mockAmbient);
+
+ mView.setContractedChild(mockContracted);
+ mView.setExpandedChild(mockExpanded);
+ mView.setHeadsUpChild(mockHeadsUp);
+ mView.setAmbientChild(mockAmbient);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+ mView.showAppOpsIcons(ops);
+
+ verify(mockContracted, times(1)).showAppOpsIcons(ops);
+ verify(mockExpanded, times(1)).showAppOpsIcons(ops);
+ verify(mockAmbient, never()).showAppOpsIcons(ops);
+ verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 972eddb..b1e1c02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -16,8 +16,16 @@
package com.android.systemui.statusbar;
+import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
+import static android.app.AppOpsManager.OP_CAMERA;
+
+import static junit.framework.Assert.assertEquals;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -33,7 +41,9 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -41,6 +51,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -51,6 +63,10 @@
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
+ @Mock
+ ForegroundServiceController mFsc;
+ @Mock
+ NotificationData.Environment mEnvironment;
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationData mNotificationData;
@@ -58,6 +74,7 @@
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
when(mMockPackageManager.checkUidPermission(
@@ -69,9 +86,11 @@
eq(UID_ALLOW_DURING_SETUP)))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- NotificationData.Environment mock = mock(NotificationData.Environment.class);
- when(mock.getGroupManager()).thenReturn(new NotificationGroupManager());
- mNotificationData = new TestableNotificationData(mock);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
+ when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+ when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+ mNotificationData = new TestableNotificationData(mEnvironment);
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
}
@@ -117,6 +136,117 @@
Assert.assertTrue(mRow.getEntry().channel != null);
}
+ @Test
+ public void testAdd_appOpsAdded() {
+ ArraySet<Integer> expected = new ArraySet<>();
+ expected.add(3);
+ expected.add(235);
+ expected.add(1);
+ when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
+ mRow.getEntry().notification.getPackageName())).thenReturn(expected);
+
+ mNotificationData.add(mRow.getEntry());
+ assertEquals(expected.size(),
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
+ for (int op : expected) {
+ assertTrue(" entry missing op " + op,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
+ }
+ }
+
+ @Test
+ public void testAdd_noExistingAppOps() {
+ when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
+ mRow.getEntry().notification.getPackageName())).thenReturn(null);
+
+ mNotificationData.add(mRow.getEntry());
+ assertEquals(0, mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
+ }
+
+ @Test
+ public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
+ mNotificationData.add(mRow.getEntry());
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ mNotificationData.add(row2.getEntry());
+ ExpandableNotificationRow diffPkg =
+ new NotificationTestHelper(getContext()).createRow("pkg", 4000);
+ mNotificationData.add(diffPkg.getEntry());
+
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_ACCEPT_HANDOVER);
+
+ for (int op : expectedOps) {
+ mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, true);
+ }
+ for (int op : expectedOps) {
+ assertTrue(mRow.getEntry().key + " doesn't have op " + op,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
+ assertTrue(row2.getEntry().key + " doesn't have op " + op,
+ mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
+ assertFalse(diffPkg.getEntry().key + " has op " + op,
+ mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
+ }
+ }
+
+ @Test
+ public void testAppOpsRemoval() throws Exception {
+ mNotificationData.add(mRow.getEntry());
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ mNotificationData.add(row2.getEntry());
+
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_ACCEPT_HANDOVER);
+
+ for (int op : expectedOps) {
+ mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, true);
+ }
+
+ expectedOps.remove(OP_ACCEPT_HANDOVER);
+ mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
+ NotificationTestHelper.PKG, false);
+
+ assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+ assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+ assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(mRow.getEntry().key)
+ .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+ assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(row2.getEntry().key)
+ .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+ }
+
+ @Test
+ public void testSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+ }
+
+ @Test
+ public void testDoNotSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index f9ec3f92..37dd939 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -23,14 +23,17 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
@@ -274,4 +277,40 @@
assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
}
+
+ @Test
+ public void testUpdateAppOps_foregroundNoti() {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+ .thenReturn("something");
+ mEntry.row = mRow;
+ mEntryManager.getNotificationData().add(mEntry);
+
+
+ mHandler.post(() -> {
+ mEntryManager.updateNotificationsForAppOps(
+ AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
+ mEntry.notification.getPackageName(), true);
+ });
+ waitForIdleSync(mHandler);
+
+ verify(mPresenter, times(1)).updateNotificationViews();
+ assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
+ AppOpsManager.OP_CAMERA));
+ }
+
+ @Test
+ public void testUpdateAppOps_otherNoti() {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+ .thenReturn(null);
+ mHandler.post(() -> {
+ mEntryManager.updateNotificationsForAppOps(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+ });
+ waitForIdleSync(mHandler);
+
+ verify(mPresenter, never()).updateNotificationViews();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index f3c1171..2764254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -48,6 +48,8 @@
private ExpandableNotificationRow mRow;
private InflationException mException;
private HeadsUpManager mHeadsUpManager;
+ protected static final String PKG = "com.android.systemui";
+ protected static final int UID = 1000;
public NotificationTestHelper(Context context) {
mContext = context;
@@ -55,7 +57,7 @@
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
}
- public ExpandableNotificationRow createRow() throws Exception {
+ public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -67,10 +69,19 @@
.setContentText("Text")
.setPublicVersion(publicVersion)
.build();
- return createRow(notification);
+ return createRow(notification, pkg, uid);
+ }
+
+ public ExpandableNotificationRow createRow() throws Exception {
+ return createRow(PKG, UID);
}
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
+ return createRow(notification, PKG, UID);
+ }
+
+ public ExpandableNotificationRow createRow(Notification notification, String pkg, int uid)
+ throws Exception {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mInstrumentation.runOnMainSync(() -> {
@@ -83,8 +94,7 @@
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(aboveShelf -> {});
UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
- StatusBarNotification sbn = new StatusBarNotification("com.android.systemui",
- "com.android.systemui", mId++, null, 1000,
+ StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, mId++, null, uid,
2000, notification, mUser, null, System.currentTimeMillis());
NotificationData.Entry entry = new NotificationData.Entry(sbn);
entry.row = row;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index fbe730a..76ed452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -19,6 +19,9 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -170,6 +173,19 @@
assertEquals(View.VISIBLE, entry1.row.getVisibility());
}
+ @Test
+ public void testUpdateNotificationViews_appOps() throws Exception {
+ NotificationData.Entry entry0 = createEntry();
+ entry0.row = spy(entry0.row);
+ when(mNotificationData.getActiveNotifications()).thenReturn(
+ Lists.newArrayList(entry0));
+ mListContainer.addContainerView(entry0.row);
+
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(entry0.row, times(1)).showAppOpsIcons(any());
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 31442af..ff545f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -69,6 +69,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
@@ -145,6 +146,7 @@
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
+ mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class));
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
new file mode 100644
index 0000000..d54c295
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BatteryControllerTest extends SysuiTestCase {
+
+ @Mock
+ private PowerManager mPowerManager;
+ private BatteryControllerImpl mBatteryController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mBatteryController = new BatteryControllerImpl(getContext(), mPowerManager);
+ }
+
+ @Test
+ public void testIndependentAODBatterySaver_true() {
+ PowerSaveState state = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(true)
+ .build();
+ Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ when(mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD)).thenReturn(state);
+ when(mPowerManager.isPowerSaveMode()).thenReturn(true);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertTrue(mBatteryController.isPowerSave());
+ Assert.assertTrue(mBatteryController.isAodPowerSave());
+ }
+
+ @Test
+ public void testIndependentAODBatterySaver_false() {
+ PowerSaveState state = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(false)
+ .build();
+ Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ when(mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD)).thenReturn(state);
+ when(mPowerManager.isPowerSaveMode()).thenReturn(true);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertTrue(mBatteryController.isPowerSave());
+ Assert.assertFalse(mBatteryController.isAodPowerSave());
+ }
+
+}
diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk
index 4c80a26..8507646 100644
--- a/packages/VpnDialogs/Android.mk
+++ b/packages/VpnDialogs/Android.mk
@@ -27,5 +27,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := VpnDialogs
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
index 60f093f..91526dd 100644
--- a/packages/WAPPushManager/Android.mk
+++ b/packages/WAPPushManager/Android.mk
@@ -9,6 +9,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := WAPPushManager
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JAVA_LIBRARIES += telephony-common
LOCAL_STATIC_JAVA_LIBRARIES += android-common
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
index bfc85ab..c4c2240f 100644
--- a/packages/WAPPushManager/tests/Android.mk
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -32,6 +32,7 @@
# automatically get all of its classes loaded into our environment.
LOCAL_PACKAGE_NAME := WAPPushManagerTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := WAPPushManager
diff --git a/packages/WallpaperBackup/Android.mk b/packages/WallpaperBackup/Android.mk
index cf04249..a6426a6 100644
--- a/packages/WallpaperBackup/Android.mk
+++ b/packages/WallpaperBackup/Android.mk
@@ -24,6 +24,7 @@
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PACKAGE_NAME := WallpaperBackup
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := false
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 7efe8ab..848f2bd 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := WallpaperCropper
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index 4f3a8b1..f5afad2 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -9,5 +9,6 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := DisplayCutoutEmulationNarrowOverlay
+LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index a584a7f..c22b2e7 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index dac3878..f1f8c27 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -9,5 +9,6 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := DisplayCutoutEmulationTallOverlay
+LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 915e164..401e092 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index f4f250c..d149d8e 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -9,5 +9,6 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWideOverlay
+LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index b8e29da..f328b83 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -46,7 +46,8 @@
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>
<!-- Height of the status bar -->
- <dimen name="status_bar_height">48dp</dimen>
+ <dimen name="status_bar_height_portrait">48dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
diff --git a/packages/overlays/SysuiDarkThemeOverlay/Android.mk b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
index 4b83058..7b277bc 100644
--- a/packages/overlays/SysuiDarkThemeOverlay/Android.mk
+++ b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
@@ -9,5 +9,6 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := SysuiDarkThemeOverlay
+LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index 3c4e951..5be90c0 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := PacProcessor
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
diff --git a/packages/services/Proxy/Android.mk b/packages/services/Proxy/Android.mk
index d5546b2..ce1715f 100644
--- a/packages/services/Proxy/Android.mk
+++ b/packages/services/Proxy/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ProxyHandler
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b897c7c..6f31b0a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5343,6 +5343,11 @@
// OS: P
ACTION_BATTERY_TIP_SHOWN = 1324;
+ // OPEN: Settings > Security & Location > Location > See all
+ // CATEGORY: SETTINGS
+ // OS: P
+ RECENT_LOCATION_REQUESTS_ALL = 1325;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk
index e0e490c..c4517a9 100644
--- a/sax/tests/saxtests/Android.mk
+++ b/sax/tests/saxtests/Android.mk
@@ -10,6 +10,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := FrameworksSaxTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 7278e83..dbed242 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -590,7 +590,7 @@
}
}
- final class AnchoredWindow implements View.OnTouchListener {
+ final class AnchoredWindow {
private final @NonNull OverlayControl mOverlayControl;
private final WindowManager mWm;
private final View mContentView;
@@ -623,7 +623,6 @@
params.accessibilityTitle = mContentView.getContext()
.getString(R.string.autofill_picker_accessibility_title);
mWm.addView(mContentView, params);
- mContentView.setOnTouchListener(this);
mOverlayControl.hideOverlays();
mShowing = true;
} else {
@@ -647,7 +646,6 @@
void hide() {
try {
if (mShowing) {
- mContentView.setOnTouchListener(null);
mWm.removeView(mContentView);
mShowing = false;
}
@@ -661,16 +659,6 @@
mOverlayControl.showOverlays();
}
}
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- // When the window is touched outside, hide the window.
- if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mCallback.onCanceled();
- return true;
- }
- return false;
- }
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 782bf71..369df54 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3555,6 +3555,7 @@
+ (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
if (mBackupRunning) pw.println("Backup currently running");
+ pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
pw.println("Last backup pass started: " + mLastBackupPass
+ " (now = " + System.currentTimeMillis() + ')');
pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 3cf374f..4443130 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -23,6 +23,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -30,6 +31,7 @@
import android.os.ParcelFileDescriptor;
import android.util.Slog;
+import com.android.server.LocalServices;
import com.android.server.backup.utils.AppBackupUtils;
import java.io.BufferedInputStream;
@@ -235,7 +237,7 @@
if (home != null) {
try {
homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
- PackageManager.GET_SIGNATURES);
+ PackageManager.GET_SIGNING_CERTIFICATES);
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
homeVersion = homeInfo.getLongVersionCode();
homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
@@ -252,10 +254,11 @@
// 2. the home app [or absence] we now use differs from the prior state,
// OR 3. it looks like we use the same home app + version as before, but
// the signatures don't match so we treat them as different apps.
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
|| !Objects.equals(home, mStoredHomeComponent)
|| (home != null
- && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo));
+ && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo, pmi));
if (needHomeBackup) {
if (DEBUG) {
Slog.i(TAG, "Home preference changed; backing up new state " + home);
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 0ca4f25..c1a1c1d 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -36,11 +36,13 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.LocalServices;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -207,8 +209,11 @@
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
info);
+ PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
- mBackupManagerService.getPackageManager(), allowApks, info, signatures);
+ mBackupManagerService.getPackageManager(), allowApks, info, signatures,
+ pmi);
mManifestSignatures.put(info.packageName, signatures);
mPackagePolicies.put(pkg, restorePolicy);
mPackageInstallers.put(pkg, info.installerPackageName);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index e576b3c..dacde0b 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -40,6 +40,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
@@ -47,6 +48,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -470,9 +472,11 @@
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
info);
+ PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mBackupManagerService.getPackageManager(), allowApks,
- info, signatures);
+ info, signatures, pmi);
mManifestSignatures.put(info.packageName, signatures);
mPackagePolicies.put(pkg, restorePolicy);
mPackageInstallers.put(pkg, info.installerPackageName);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 3caa1e7..4b467e5 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -43,6 +43,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Message;
@@ -57,6 +58,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -504,7 +506,7 @@
try {
mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
- pkgName, PackageManager.GET_SIGNATURES);
+ pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
} catch (NameNotFoundException e) {
// Whoops, we thought we could restore this package but it
// turns out not to be present. Skip it.
@@ -619,7 +621,8 @@
}
Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
- if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage, pmi)) {
Slog.w(TAG, "Signature mismatch restoring " + packageName);
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH, mCurrentPackage,
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 6780563..90c1387 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -25,6 +25,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Process;
import android.util.Slog;
@@ -37,6 +38,9 @@
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
*/
public class AppBackupUtils {
+
+ private static final boolean DEBUG = false;
+
/**
* Returns whether app is eligible for backup.
*
@@ -88,7 +92,8 @@
public static boolean appIsRunningAndEligibleForBackupWithTransport(
@Nullable TransportClient transportClient, String packageName, PackageManager pm) {
try {
- PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if (!appIsEligibleForBackup(applicationInfo, pm)
|| appIsStopped(applicationInfo)
@@ -165,12 +170,18 @@
*
* <ul>
* <li>Source and target have at least one signature each
- * <li>Target contains all signatures in source
+ * <li>Target contains all signatures in source, and nothing more
* </ul>
*
+ * or if both source and target have exactly one signature, and they don't match, we check
+ * if the app was ever signed with source signature (i.e. app has rotated key)
+ * Note: key rotation is only supported for apps ever signed with one key, and those apps will
+ * not be allowed to be signed by more certificates in the future
+ *
* Note that if {@param target} is null we return false.
*/
- public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
+ public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target,
+ PackageManagerInternal pmi) {
if (target == null) {
return false;
}
@@ -187,33 +198,52 @@
return true;
}
- Signature[] deviceSigs = target.signatures;
- if (MORE_DEBUG) {
- Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs);
- }
-
// Don't allow unsigned apps on either end
- if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) {
+ if (ArrayUtils.isEmpty(storedSigs)) {
return false;
}
- // Signatures can be added over time, so the target-device apk needs to contain all the
- // source-device apk signatures, but not necessarily the other way around.
- int nStored = storedSigs.length;
- int nDevice = deviceSigs.length;
+ Signature[][] deviceHistorySigs = target.signingCertificateHistory;
+ if (ArrayUtils.isEmpty(deviceHistorySigs)) {
+ Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+ " PackageManager#GET_SIGNING_CERTIFICATES was not specified");
+ return false;
+ }
- for (int i = 0; i < nStored; i++) {
- boolean match = false;
- for (int j = 0; j < nDevice; j++) {
- if (storedSigs[i].equals(deviceSigs[j])) {
- match = true;
- break;
+ if (DEBUG) {
+ Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceHistorySigs);
+ }
+
+ final int nStored = storedSigs.length;
+ if (nStored == 1) {
+ // if the app is only signed with one sig, it's possible it has rotated its key
+ // (the checks with signing history are delegated to PackageManager)
+ // TODO: address the case that app has declared restoreAnyVersion and is restoring
+ // from higher version to lower after having rotated the key (i.e. higher version has
+ // different sig than lower version that we want to restore to)
+ return pmi.isDataRestoreSafe(storedSigs[0], target.packageName);
+ } else {
+ // the app couldn't have rotated keys, since it was signed with multiple sigs - do
+ // a comprehensive 1-to-1 signatures check
+ // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
+ Signature[] deviceSigs = deviceHistorySigs[0];
+ int nDevice = deviceSigs.length;
+
+ // ensure that each stored sig matches an on-device sig
+ for (int i = 0; i < nStored; i++) {
+ boolean match = false;
+ for (int j = 0; j < nDevice; j++) {
+ if (storedSigs[i].equals(deviceSigs[j])) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
}
}
- if (!match) {
- return false;
- }
+ // we have found a match for all stored sigs
+ return true;
}
- return true;
}
}
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index 10f0695..df7e6d4 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageInstaller.Session;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.IBinder;
@@ -37,6 +38,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.restore.RestoreDeleteObserver;
import com.android.server.backup.restore.RestorePolicy;
@@ -142,9 +144,8 @@
uninstall = true;
} else {
try {
- PackageInfo pkg = packageManager.getPackageInfo(
- info.packageName,
- PackageManager.GET_SIGNATURES);
+ PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
== 0) {
Slog.w(TAG, "Restore stream contains apk of package "
@@ -154,7 +155,9 @@
} else {
// So far so good -- do the signatures match the manifest?
Signature[] sigs = manifestSignatures.get(info.packageName);
- if (AppBackupUtils.signaturesMatch(sigs, pkg)) {
+ PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (AppBackupUtils.signaturesMatch(sigs, pkg, pmi)) {
// If this is a system-uid app without a declared backup agent,
// don't restore any of the file data.
if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index cc26ff8..6dd5284 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -50,6 +50,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.Process;
@@ -385,7 +386,8 @@
* @return a restore policy constant.
*/
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
- boolean allowApks, FileMetadata info, Signature[] signatures) {
+ boolean allowApks, FileMetadata info, Signature[] signatures,
+ PackageManagerInternal pmi) {
if (signatures == null) {
return RestorePolicy.IGNORE;
}
@@ -395,7 +397,7 @@
// Okay, got the manifest info we need...
try {
PackageInfo pkgInfo = packageManager.getPackageInfo(
- info.packageName, PackageManager.GET_SIGNATURES);
+ info.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
// Fall through to IGNORE if the app explicitly disallows backup
final int flags = pkgInfo.applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
@@ -411,7 +413,7 @@
// such packages are signed with the platform cert instead of
// the app developer's cert, so they're different on every
// device.
- if (AppBackupUtils.signaturesMatch(signatures, pkgInfo)) {
+ if (AppBackupUtils.signaturesMatch(signatures, pkgInfo, pmi)) {
if ((pkgInfo.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 62a7b8f..d17ca7f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -2191,42 +2191,42 @@
synchronized (mLock) {
final long nowRTC = System.currentTimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
- proto.write(AlarmManagerServiceProto.CURRENT_TIME, nowRTC);
- proto.write(AlarmManagerServiceProto.ELAPSED_REALTIME, nowElapsed);
- proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_CLOCK_TIME,
+ proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC);
+ proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed);
+ proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME,
mLastTimeChangeClockTime);
- proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_REALTIME,
+ proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_REALTIME,
mLastTimeChangeRealtime);
- mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
+ mConstants.dumpProto(proto, AlarmManagerServiceDumpProto.SETTINGS);
if (mAppStateTracker != null) {
mAppStateTracker.dumpProto(proto,
- AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+ AlarmManagerServiceDumpProto.FORCE_APP_STANDBY_TRACKER);
}
- proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
+ proto.write(AlarmManagerServiceDumpProto.IS_INTERACTIVE, mInteractive);
if (!mInteractive) {
// Durations
- proto.write(AlarmManagerServiceProto.TIME_SINCE_NON_INTERACTIVE_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_NON_INTERACTIVE_MS,
nowElapsed - mNonInteractiveStartTime);
- proto.write(AlarmManagerServiceProto.MAX_WAKEUP_DELAY_MS,
+ proto.write(AlarmManagerServiceDumpProto.MAX_WAKEUP_DELAY_MS,
currentNonWakeupFuzzLocked(nowElapsed));
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_DISPATCH_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_DISPATCH_MS,
nowElapsed - mLastAlarmDeliveryTime);
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
nowElapsed - mNextNonWakeupDeliveryTime);
}
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
mNextNonWakeup - nowElapsed);
- proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_WAKEUP_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_UNTIL_NEXT_WAKEUP_MS,
mNextWakeup - nowElapsed);
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_MS,
nowElapsed - mLastWakeup);
- proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
+ proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
nowElapsed - mLastWakeupSet);
- proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
+ proto.write(AlarmManagerServiceDumpProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
final TreeSet<Integer> users = new TreeSet<>();
final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
@@ -2242,14 +2242,14 @@
final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
final long time = next != null ? next.getTriggerTime() : 0;
final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
- final long aToken = proto.start(AlarmManagerServiceProto.NEXT_ALARM_CLOCK_METADATA);
+ final long aToken = proto.start(AlarmManagerServiceDumpProto.NEXT_ALARM_CLOCK_METADATA);
proto.write(AlarmClockMetadataProto.USER, user);
proto.write(AlarmClockMetadataProto.IS_PENDING_SEND, pendingSend);
proto.write(AlarmClockMetadataProto.TRIGGER_TIME_MS, time);
proto.end(aToken);
}
for (Batch b : mAlarmBatches) {
- b.writeToProto(proto, AlarmManagerServiceProto.PENDING_ALARM_BATCHES,
+ b.writeToProto(proto, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES,
nowElapsed, nowRTC);
}
for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
@@ -2257,66 +2257,66 @@
if (blockedAlarms != null) {
for (Alarm a : blockedAlarms) {
a.writeToProto(proto,
- AlarmManagerServiceProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
+ AlarmManagerServiceDumpProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
nowElapsed, nowRTC);
}
}
}
if (mPendingIdleUntil != null) {
mPendingIdleUntil.writeToProto(
- proto, AlarmManagerServiceProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
+ proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
}
for (Alarm a : mPendingWhileIdleAlarms) {
- a.writeToProto(proto, AlarmManagerServiceProto.PENDING_WHILE_IDLE_ALARMS,
+ a.writeToProto(proto, AlarmManagerServiceDumpProto.PENDING_WHILE_IDLE_ALARMS,
nowElapsed, nowRTC);
}
if (mNextWakeFromIdle != null) {
- mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceProto.NEXT_WAKE_FROM_IDLE,
+ mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceDumpProto.NEXT_WAKE_FROM_IDLE,
nowElapsed, nowRTC);
}
for (Alarm a : mPendingNonWakeupAlarms) {
- a.writeToProto(proto, AlarmManagerServiceProto.PAST_DUE_NON_WAKEUP_ALARMS,
+ a.writeToProto(proto, AlarmManagerServiceDumpProto.PAST_DUE_NON_WAKEUP_ALARMS,
nowElapsed, nowRTC);
}
- proto.write(AlarmManagerServiceProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
- proto.write(AlarmManagerServiceProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
- proto.write(AlarmManagerServiceProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
- proto.write(AlarmManagerServiceProto.MAX_NON_INTERACTIVE_DURATION_MS,
+ proto.write(AlarmManagerServiceDumpProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
+ proto.write(AlarmManagerServiceDumpProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
+ proto.write(AlarmManagerServiceDumpProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
+ proto.write(AlarmManagerServiceDumpProto.MAX_NON_INTERACTIVE_DURATION_MS,
mNonInteractiveTime);
- proto.write(AlarmManagerServiceProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
- proto.write(AlarmManagerServiceProto.PENDING_INTENT_SEND_COUNT, mSendCount);
- proto.write(AlarmManagerServiceProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
- proto.write(AlarmManagerServiceProto.LISTENER_SEND_COUNT, mListenerCount);
- proto.write(AlarmManagerServiceProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
+ proto.write(AlarmManagerServiceDumpProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
+ proto.write(AlarmManagerServiceDumpProto.PENDING_INTENT_SEND_COUNT, mSendCount);
+ proto.write(AlarmManagerServiceDumpProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
+ proto.write(AlarmManagerServiceDumpProto.LISTENER_SEND_COUNT, mListenerCount);
+ proto.write(AlarmManagerServiceDumpProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
for (InFlight f : mInFlight) {
- f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
+ f.writeToProto(proto, AlarmManagerServiceDumpProto.OUTSTANDING_DELIVERIES);
}
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
final long token = proto.start(
- AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
+ AlarmManagerServiceDumpProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID, uid);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid);
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+ proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
lastTime + getWhileIdleMinIntervalLocked(uid));
proto.end(token);
}
for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- proto.write(AlarmManagerServiceProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
+ proto.write(AlarmManagerServiceDumpProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
mUseAllowWhileIdleShortTime.keyAt(i));
}
}
- mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
+ mLog.writeToProto(proto, AlarmManagerServiceDumpProto.RECENT_PROBLEMS);
final FilterStats[] topFilters = new FilterStats[10];
final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
@@ -2357,13 +2357,13 @@
}
}
for (int i = 0; i < len; ++i) {
- final long token = proto.start(AlarmManagerServiceProto.TOP_ALARMS);
+ final long token = proto.start(AlarmManagerServiceDumpProto.TOP_ALARMS);
FilterStats fs = topFilters[i];
- proto.write(AlarmManagerServiceProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
- proto.write(AlarmManagerServiceProto.TopAlarm.PACKAGE_NAME,
+ proto.write(AlarmManagerServiceDumpProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
+ proto.write(AlarmManagerServiceDumpProto.TopAlarm.PACKAGE_NAME,
fs.mBroadcastStats.mPackageName);
- fs.writeToProto(proto, AlarmManagerServiceProto.TopAlarm.FILTER);
+ fs.writeToProto(proto, AlarmManagerServiceDumpProto.TopAlarm.FILTER);
proto.end(token);
}
@@ -2372,10 +2372,10 @@
for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
for (int ip = 0; ip < uidStats.size(); ++ip) {
- final long token = proto.start(AlarmManagerServiceProto.ALARM_STATS);
+ final long token = proto.start(AlarmManagerServiceDumpProto.ALARM_STATS);
BroadcastStats bs = uidStats.valueAt(ip);
- bs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.BROADCAST);
+ bs.writeToProto(proto, AlarmManagerServiceDumpProto.AlarmStat.BROADCAST);
// uidStats is an ArrayMap, which we can't sort.
tmpFilters.clear();
@@ -2384,7 +2384,7 @@
}
Collections.sort(tmpFilters, comparator);
for (FilterStats fs : tmpFilters) {
- fs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.FILTERS);
+ fs.writeToProto(proto, AlarmManagerServiceDumpProto.AlarmStat.FILTERS);
}
proto.end(token);
@@ -2395,7 +2395,7 @@
for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
final long token = proto.start(
- AlarmManagerServiceProto.ALLOW_WHILE_IDLE_DISPATCHES);
+ AlarmManagerServiceDumpProto.ALLOW_WHILE_IDLE_DISPATCHES);
proto.write(IdleDispatchEntryProto.UID, ent.uid);
proto.write(IdleDispatchEntryProto.PKG, ent.pkg);
@@ -2411,7 +2411,7 @@
if (WAKEUP_STATS) {
for (WakeupEvent event : mRecentWakeups) {
- final long token = proto.start(AlarmManagerServiceProto.RECENT_WAKEUP_HISTORY);
+ final long token = proto.start(AlarmManagerServiceDumpProto.RECENT_WAKEUP_HISTORY);
proto.write(WakeupEventProto.UID, event.uid);
proto.write(WakeupEventProto.ACTION, event.action);
proto.write(WakeupEventProto.WHEN, event.when);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ca67a34..53c9ecb 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -211,9 +211,11 @@
public final class ActiveCallback implements DeathRecipient {
final IAppOpsActiveCallback mCallback;
+ final int mUid;
- public ActiveCallback(IAppOpsActiveCallback callback) {
+ public ActiveCallback(IAppOpsActiveCallback callback, int uid) {
mCallback = callback;
+ mUid = uid;
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -230,24 +232,22 @@
}
}
- final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
public final class ClientState extends Binder implements DeathRecipient {
+ final ArrayList<Op> mStartedOps = new ArrayList<>();
final IBinder mAppToken;
final int mPid;
- final ArrayList<Op> mStartedOps;
public ClientState(IBinder appToken) {
mAppToken = appToken;
mPid = Binder.getCallingPid();
- if (appToken instanceof Binder) {
- // For local clients, there is no reason to track them.
- mStartedOps = null;
- } else {
- mStartedOps = new ArrayList<Op>();
+ // Watch only for remote processes dying
+ if (!(appToken instanceof Binder)) {
try {
mAppToken.linkToDeath(this, 0);
} catch (RemoteException e) {
+ /* do nothing */
}
}
}
@@ -256,7 +256,7 @@
public String toString() {
return "ClientState{" +
"mAppToken=" + mAppToken +
- ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+ ", " + "pid=" + mPid +
'}';
}
@@ -264,7 +264,7 @@
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
- finishOperationLocked(mStartedOps.get(i));
+ finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true);
}
mClients.remove(mAppToken);
}
@@ -397,6 +397,27 @@
mUidStates.remove(uid);
}
+ // Finish ops other packages started on behalf of the package.
+ final int clientCount = mClients.size();
+ for (int i = 0; i < clientCount; i++) {
+ final ClientState client = mClients.valueAt(i);
+ if (client.mStartedOps == null) {
+ continue;
+ }
+ final int opCount = client.mStartedOps.size();
+ for (int j = opCount - 1; j >= 0; j--) {
+ final Op op = client.mStartedOps.get(j);
+ if (uid == op.uid && packageName.equals(op.packageName)) {
+ finishOperationLocked(op, /*finishNested*/ true);
+ client.mStartedOps.remove(j);
+ if (op.nesting <= 0) {
+ scheduleOpActiveChangedIfNeededLocked(op.op,
+ uid, packageName, false);
+ }
+ }
+ }
+ }
+
if (ops != null) {
scheduleFastWriteLocked();
@@ -1195,8 +1216,11 @@
@Override
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
- "startWatchingActive");
+ int watchedUid = -1;
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = Binder.getCallingUid();
+ }
if (ops != null) {
Preconditions.checkArrayElementsInRange(ops, 0,
AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
@@ -1210,7 +1234,7 @@
callbacks = new SparseArray<>();
mActiveWatchers.put(callback.asBinder(), callbacks);
}
- final ActiveCallback activeCallback = new ActiveCallback(callback);
+ final ActiveCallback activeCallback = new ActiveCallback(callback, watchedUid);
for (int op : ops) {
callbacks.put(op, activeCallback);
}
@@ -1239,7 +1263,8 @@
}
@Override
- public int startOperation(IBinder token, int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid, String packageName,
+ boolean startIfModeDefault) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1265,7 +1290,8 @@
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
final int uidMode = uidState.opModes.get(switchCode);
- if (uidMode != AppOpsManager.MODE_ALLOWED) {
+ if (uidMode != AppOpsManager.MODE_ALLOWED
+ && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
@@ -1274,7 +1300,8 @@
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && (!startIfModeDefault || switchOp.mode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
@@ -1316,13 +1343,11 @@
if (op == null) {
return;
}
- if (client.mStartedOps != null) {
- if (!client.mStartedOps.remove(op)) {
- throw new IllegalStateException("Operation not started: uid" + op.uid
- + " pkg=" + op.packageName + " op=" + op.op);
- }
+ if (!client.mStartedOps.remove(op)) {
+ throw new IllegalStateException("Operation not started: uid" + op.uid
+ + " pkg=" + op.packageName + " op=" + op.op);
}
- finishOperationLocked(op);
+ finishOperationLocked(op, /*finishNested*/ false);
if (op.nesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
}
@@ -1337,6 +1362,9 @@
final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
ActiveCallback callback = callbacks.get(code);
if (callback != null) {
+ if (callback.mUid >= 0 && callback.mUid != uid) {
+ continue;
+ }
if (dispatchedCallbacks == null) {
dispatchedCallbacks = new ArraySet<>();
}
@@ -1380,9 +1408,9 @@
return AppOpsManager.permissionToOpCode(permission);
}
- void finishOperationLocked(Op op) {
- if (op.nesting <= 1) {
- if (op.nesting == 1) {
+ void finishOperationLocked(Op op, boolean finishNested) {
+ if (op.nesting <= 1 || finishNested) {
+ if (op.nesting == 1 || finishNested) {
op.duration = (int)(System.currentTimeMillis() - op.time);
op.time += op.duration;
} else {
@@ -2420,7 +2448,7 @@
pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
ClientState cs = mClients.valueAt(i);
pw.print(" "); pw.println(cs);
- if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+ if (cs.mStartedOps.size() > 0) {
pw.println(" Started ops:");
for (int j=0; j<cs.mStartedOps.size(); j++) {
Op op = cs.mStartedOps.get(j);
@@ -2651,8 +2679,12 @@
@Override
public boolean isOperationActive(int code, int uid, String packageName) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
- "isOperationActive");
+ if (Binder.getCallingUid() != uid) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
verifyIncomingOp(code);
final String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -2661,8 +2693,6 @@
synchronized (AppOpsService.this) {
for (int i = mClients.size() - 1; i >= 0; i--) {
final ClientState client = mClients.valueAt(i);
- if (client.mStartedOps == null) continue;
-
for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
final Op op = client.mStartedOps.get(j);
if (op.op == code && op.uid == uid) return true;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 14c99b2..752c44a 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -98,7 +98,7 @@
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
- private final IAppOpsService mAppOpsService;
+ private final AppOpsManager mAppOps;
private final IBatteryStats mBatteryStatsService;
private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
@@ -265,8 +265,7 @@
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
- mAppOpsService =
- IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -721,17 +720,10 @@
}
private int getAppOpMode(Vibration vib) {
- int mode;
- try {
- mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
- vib.usageHint, vib.uid, vib.opPkg);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get appop mode for vibration!", e);
- mode = AppOpsManager.MODE_IGNORED;
+ int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
+ vib.usageHint, vib.uid, vib.opPkg);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
}
return mode;
}
@@ -741,11 +733,8 @@
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
try {
if (mCurrentVibration != null) {
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
- mCurrentVibration.opPkg);
- } catch (RemoteException e) { }
+ mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+ mCurrentVibration.opPkg);
unlinkVibration(mCurrentVibration);
mCurrentVibration = null;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5fc4373..0c6746e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3861,8 +3861,9 @@
return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
}
- protected void writeToProto(ProtoOutputStream proto) {
+ protected void writeToProto(ProtoOutputStream proto, long fieldId) {
synchronized (mAm) {
+ final long outterToken = proto.start(fieldId);
int[] users = mAm.mUserController.getUsers();
for (int user : users) {
ServiceMap smap = mServiceMap.get(user);
@@ -3878,6 +3879,7 @@
}
proto.end(token);
}
+ proto.end(outterToken);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e1c70f9..28a79bd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -440,15 +440,17 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.am.proto.ActivityManagerServiceProto;
-import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpServicesProto;
import com.android.server.am.proto.GrantUriProto;
import com.android.server.am.proto.ImportanceTokenProto;
-import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.proto.MemInfoDumpProto;
import com.android.server.am.proto.NeededUriGrantsProto;
import com.android.server.am.proto.ProcessOomProto;
import com.android.server.am.proto.ProcessToGcProto;
-import com.android.server.am.proto.ProcessesProto;
-import com.android.server.am.proto.ProcessesProto.UidObserverRegistrationProto;
import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -1376,9 +1378,9 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(ProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
- proto.write(ProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
- proto.write(ProcessesProto.PendingTempWhitelist.TAG, tag);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
proto.end(token);
}
}
@@ -5140,7 +5142,6 @@
public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
IRecentsAnimationRunner recentsAnimationRunner) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
- final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -5166,7 +5167,7 @@
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
- mActivityStartController, mWindowManager, mUserController, callingPid);
+ mActivityStartController, mWindowManager, mUserController);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
recentsUid);
}
@@ -6491,22 +6492,27 @@
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
+ final int[] userIds = mUserController.expandUserId(userId);
+
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- synchronized(this) {
+ for (int targetUserId : userIds) {
int appId = -1;
try {
appId = UserHandle.getAppId(
- pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId));
+ pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+ targetUserId));
} catch (RemoteException e) {
}
if (appId == -1) {
Slog.w(TAG, "Invalid packageName: " + packageName);
return;
}
- killPackageProcessesLocked(packageName, appId, userId,
- ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
+ synchronized (this) {
+ killPackageProcessesLocked(packageName, appId, targetUserId,
+ ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
+ }
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -12875,6 +12881,25 @@
}
@Override
+ public boolean isBackgroundRestricted(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ final int packageUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.getUserId(callingUid));
+ if (packageUid != callingUid) {
+ throw new IllegalArgumentException("Uid " + callingUid
+ + " cannot query restriction state for package " + packageName);
+ }
+ } catch (RemoteException exc) {
+ // Ignore.
+ }
+ final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ callingUid, packageName);
+ return (mode != AppOpsManager.MODE_ALLOWED);
+ }
+
+ @Override
public void backgroundWhitelistUid(final int uid) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
@@ -14329,28 +14354,6 @@
}
}
- void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
- synchronized (ActivityManagerService.this) {
- final ProcessRecord pr;
- synchronized (mPidsSelfLocked) {
- pr = mPidsSelfLocked.get(pid);
- if (pr == null) {
- Slog.w(TAG, "setRunningRemoteAnimation called on unknown pid: " + pid);
- return;
- }
- }
- if (pr.runningRemoteAnimation == runningRemoteAnimation) {
- return;
- }
- pr.runningRemoteAnimation = runningRemoteAnimation;
- if (DEBUG_OOM_ADJ) {
- Slog.i(TAG, "Setting runningRemoteAnimation=" + pr.runningRemoteAnimation
- + " for pid=" + pid);
- }
- updateOomAdjLocked(pr, true);
- }
- }
-
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready
@@ -15772,12 +15775,12 @@
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
- // output proto is ActivityStackSupervisorProto
+ // output proto is ActivityManagerServiceDumpActivitiesProto
synchronized (this) {
writeActivitiesToProtoLocked(proto);
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
- // output proto is BroadcastProto
+ // output proto is ActivityManagerServiceDumpBroadcastsProto
synchronized (this) {
writeBroadcastsToProtoLocked(proto);
}
@@ -15799,7 +15802,8 @@
pw.println("Use -h for help.");
}
} else if ("service".equals(cmd)) {
- mServices.writeToProto(proto);
+ // output proto is ActivityManagerServiceDumpServicesProto
+ mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
@@ -15821,7 +15825,7 @@
proto.end(broadcastToken);
long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
- mServices.writeToProto(proto);
+ mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
@@ -16184,8 +16188,8 @@
}
private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
- // The output proto of "activity --proto activities" is ActivityStackSupervisorProto
- mStackSupervisor.writeToProto(proto);
+ // The output proto of "activity --proto activities" is ActivityManagerServiceDumpActivitiesProto
+ mStackSupervisor.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
private void dumpLastANRLocked(PrintWriter pw) {
@@ -16882,7 +16886,7 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
if (r.persistent) {
numPers++;
}
@@ -16894,7 +16898,7 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.ISOLATED_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS);
}
for (int i=0; i<mActiveInstrumentation.size(); i++) {
@@ -16903,7 +16907,7 @@
&& !ai.mTargetInfo.packageName.equals(dumpPackage)) {
continue;
}
- ai.writeToProto(proto, ProcessesProto.ACTIVE_INSTRUMENTATIONS);
+ ai.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
}
int whichAppId = getAppId(dumpPackage);
@@ -16912,7 +16916,7 @@
if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
continue;
}
- uidRec.writeToProto(proto, ProcessesProto.ACTIVE_UIDS);
+ uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
}
for (int i=0; i<mValidateUids.size(); i++) {
@@ -16920,16 +16924,16 @@
if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
continue;
}
- uidRec.writeToProto(proto, ProcessesProto.VALIDATE_UIDS);
+ uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
}
if (mLruProcesses.size() > 0) {
- long lruToken = proto.start(ProcessesProto.LRU_PROCS);
+ long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
int total = mLruProcesses.size();
- proto.write(ProcessesProto.LruProcesses.SIZE, total);
- proto.write(ProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
- proto.write(ProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
- writeProcessOomListToProto(proto, ProcessesProto.LruProcesses.LIST, this,
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
+ writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
mLruProcesses,false, dumpPackage);
proto.end(lruToken);
}
@@ -16941,7 +16945,7 @@
if (!r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PIDS_SELF_LOCKED);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
}
}
}
@@ -16955,7 +16959,7 @@
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
- it.writeToProto(proto, ProcessesProto.IMPORTANT_PROCS);
+ it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
}
}
}
@@ -16965,7 +16969,7 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.PERSISTENT_STARTING_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
}
for (int i=0; i<mRemovedProcesses.size(); i++) {
@@ -16973,7 +16977,7 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.REMOVED_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.REMOVED_PROCS);
}
for (int i=0; i<mProcessesOnHold.size(); i++) {
@@ -16981,41 +16985,41 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ProcessesProto.ON_HOLD_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS);
}
- writeProcessesToGcToProto(proto, ProcessesProto.GC_PROCS, dumpPackage);
- mAppErrors.writeToProto(proto, ProcessesProto.APP_ERRORS, dumpPackage);
+ writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
+ mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
if (dumpPackage == null) {
- mUserController.writeToProto(proto, ProcessesProto.USER_CONTROLLER);
- getGlobalConfiguration().writeToProto(proto, ProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+ mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
+ getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
}
if (mHomeProcess != null && (dumpPackage == null
|| mHomeProcess.pkgList.containsKey(dumpPackage))) {
- mHomeProcess.writeToProto(proto, ProcessesProto.HOME_PROC);
+ mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
}
if (mPreviousProcess != null && (dumpPackage == null
|| mPreviousProcess.pkgList.containsKey(dumpPackage))) {
- mPreviousProcess.writeToProto(proto, ProcessesProto.PREVIOUS_PROC);
- proto.write(ProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
|| mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
- mHeavyWeightProcess.writeToProto(proto, ProcessesProto.HEAVY_WEIGHT_PROC);
+ mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
}
for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage == null || dumpPackage.equals(pkg)) {
- long compatToken = proto.start(ProcessesProto.SCREEN_COMPAT_PACKAGES);
- proto.write(ProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
- proto.write(ProcessesProto.ScreenCompatPackage.MODE, mode);
+ long compatToken = proto.start(ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE, mode);
proto.end(compatToken);
}
}
@@ -17025,89 +17029,89 @@
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
- reg.writeToProto(proto, ProcessesProto.UID_OBSERVERS);
+ reg.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
for (int v : mDeviceIdleWhitelist) {
- proto.write(ProcessesProto.DEVICE_IDLE_WHITELIST, v);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v);
}
for (int v : mDeviceIdleTempWhitelist) {
- proto.write(ProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
}
if (mPendingTempWhitelist.size() > 0) {
for (int i=0; i < mPendingTempWhitelist.size(); i++) {
mPendingTempWhitelist.valueAt(i).writeToProto(proto,
- ProcessesProto.PENDING_TEMP_WHITELIST);
+ ActivityManagerServiceDumpProcessesProto.PENDING_TEMP_WHITELIST);
}
}
if (dumpPackage == null) {
- final long sleepToken = proto.start(ProcessesProto.SLEEP_STATUS);
- proto.write(ProcessesProto.SleepStatus.WAKEFULNESS,
+ final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
for (SleepToken st : mStackSupervisor.mSleepTokens) {
- proto.write(ProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
}
- proto.write(ProcessesProto.SleepStatus.SLEEPING, mSleeping);
- proto.write(ProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
- proto.write(ProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
proto.end(sleepToken);
if (mRunningVoice != null) {
- final long vrToken = proto.start(ProcessesProto.RUNNING_VOICE);
- proto.write(ProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
- mVoiceWakeLock.writeToProto(proto, ProcessesProto.VoiceProto.WAKELOCK);
+ final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
+ mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VoiceProto.WAKELOCK);
proto.end(vrToken);
}
- mVrController.writeToProto(proto, ProcessesProto.VR_CONTROLLER);
+ mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
|| dumpPackage.equals(mOrigDebugApp)) {
- final long debugAppToken = proto.start(ProcessesProto.DEBUG);
- proto.write(ProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
- proto.write(ProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
- proto.write(ProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
- proto.write(ProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
+ final long debugAppToken = proto.start(ActivityManagerServiceDumpProcessesProto.DEBUG);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
+ proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
proto.end(debugAppToken);
}
}
if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.writeToProto(proto, ProcessesProto.CURRENT_TRACKER, true);
+ mCurAppTimeTracker.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
}
if (mMemWatchProcesses.getMap().size() > 0) {
- final long token = proto.start(ProcessesProto.MEM_WATCH_PROCESSES);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
for (int i=0; i<procs.size(); i++) {
final String proc = procs.keyAt(i);
final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
- final long ptoken = proto.start(ProcessesProto.MemWatchProcess.PROCS);
- proto.write(ProcessesProto.MemWatchProcess.Process.NAME, proc);
+ final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc);
for (int j=0; j<uids.size(); j++) {
- final long utoken = proto.start(ProcessesProto.MemWatchProcess.Process.MEM_STATS);
+ final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MEM_STATS);
Pair<Long, String> val = uids.valueAt(j);
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
DebugUtils.sizeValueToString(val.first, new StringBuilder()));
- proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
proto.end(utoken);
}
proto.end(ptoken);
}
- final long dtoken = proto.start(ProcessesProto.MemWatchProcess.DUMP);
- proto.write(ProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
- proto.write(ProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
- proto.write(ProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
- proto.write(ProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
+ final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
proto.end(dtoken);
proto.end(token);
@@ -17115,58 +17119,58 @@
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
- proto.write(ProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
}
}
if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
(mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
- final long token = proto.start(ProcessesProto.PROFILE);
- proto.write(ProcessesProto.Profile.APP_NAME, mProfileApp);
- mProfileProc.writeToProto(proto,ProcessesProto.Profile.PROC);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME, mProfileApp);
+ mProfileProc.writeToProto(proto,ActivityManagerServiceDumpProcessesProto.Profile.PROC);
if (mProfilerInfo != null) {
- mProfilerInfo.writeToProto(proto, ProcessesProto.Profile.INFO);
- proto.write(ProcessesProto.Profile.TYPE, mProfileType);
+ mProfilerInfo.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Profile.INFO);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE, mProfileType);
}
proto.end(token);
}
}
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
- proto.write(ProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
}
if (dumpPackage == null) {
- proto.write(ProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
if (mController != null) {
- final long token = proto.start(ProcessesProto.CONTROLLER);
- proto.write(ProcessesProto.Controller.CONTROLLER, mController.toString());
- proto.write(ProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
proto.end(token);
}
- proto.write(ProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
- proto.write(ProcessesProto.PROCESSES_READY, mProcessesReady);
- proto.write(ProcessesProto.SYSTEM_READY, mSystemReady);
- proto.write(ProcessesProto.BOOTED, mBooted);
- proto.write(ProcessesProto.FACTORY_TEST, mFactoryTest);
- proto.write(ProcessesProto.BOOTING, mBooting);
- proto.write(ProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
- proto.write(ProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
- proto.write(ProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
- mStackSupervisor.mGoingToSleep.writeToProto(proto, ProcessesProto.GOING_TO_SLEEP);
- mStackSupervisor.mLaunchingActivity.writeToProto(proto, ProcessesProto.LAUNCHING_ACTIVITY);
- proto.write(ProcessesProto.ADJ_SEQ, mAdjSeq);
- proto.write(ProcessesProto.LRU_SEQ, mLruSeq);
- proto.write(ProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
- proto.write(ProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
- proto.write(ProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
- proto.write(ProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
- proto.write(ProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
- proto.write(ProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+ proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PROCESSES_READY, mProcessesReady);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SYSTEM_READY, mSystemReady);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOTED, mBooted);
+ proto.write(ActivityManagerServiceDumpProcessesProto.FACTORY_TEST, mFactoryTest);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOTING, mBooting);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
+ proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
+ mStackSupervisor.mGoingToSleep.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP);
+ mStackSupervisor.mLaunchingActivity.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mLruSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
+ proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
long now = SystemClock.uptimeMillis();
- ProtoUtils.toDuration(proto, ProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
- proto.write(ProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
+ ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
}
}
@@ -17476,15 +17480,15 @@
Iterator it = mRegisteredReceivers.values().iterator();
while (it.hasNext()) {
ReceiverList r = (ReceiverList)it.next();
- r.writeToProto(proto, BroadcastProto.RECEIVER_LIST);
+ r.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_LIST);
}
}
- mReceiverResolver.writeToProto(proto, BroadcastProto.RECEIVER_RESOLVER);
+ mReceiverResolver.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
for (BroadcastQueue q : mBroadcastQueues) {
- q.writeToProto(proto, BroadcastProto.BROADCAST_QUEUE);
+ q.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
}
for (int user=0; user<mStickyBroadcasts.size(); user++) {
- long token = proto.start(BroadcastProto.STICKY_BROADCASTS);
+ long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
for (Map.Entry<String, ArrayList<Intent>> ent
: mStickyBroadcasts.valueAt(user).entrySet()) {
@@ -17499,9 +17503,10 @@
proto.end(token);
}
- long handlerToken = proto.start(BroadcastProto.HANDLER);
- proto.write(BroadcastProto.MainHandler.HANDLER, mHandler.toString());
- mHandler.getLooper().writeToProto(proto, BroadcastProto.MainHandler.LOOPER);
+ long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
+ proto.write(ActivityManagerServiceDumpBroadcastsProto.MainHandler.HANDLER, mHandler.toString());
+ mHandler.getLooper().writeToProto(proto,
+ ActivityManagerServiceDumpBroadcastsProto.MainHandler.LOOPER);
proto.end(handlerToken);
}
@@ -18263,17 +18268,17 @@
MemItem mi = items.get(i);
final long token = proto.start(fieldId);
- proto.write(MemInfoProto.MemItem.TAG, tag);
- proto.write(MemInfoProto.MemItem.LABEL, mi.shortLabel);
- proto.write(MemInfoProto.MemItem.IS_PROC, mi.isProc);
- proto.write(MemInfoProto.MemItem.ID, mi.id);
- proto.write(MemInfoProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
- proto.write(MemInfoProto.MemItem.PSS_KB, mi.pss);
+ proto.write(MemInfoDumpProto.MemItem.TAG, tag);
+ proto.write(MemInfoDumpProto.MemItem.LABEL, mi.shortLabel);
+ proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc);
+ proto.write(MemInfoDumpProto.MemItem.ID, mi.id);
+ proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
+ proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
if (dumpSwapPss) {
- proto.write(MemInfoProto.MemItem.SWAP_PSS_KB, mi.swapPss);
+ proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss);
}
if (mi.subitems != null) {
- dumpMemItems(proto, MemInfoProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems,
+ dumpMemItems(proto, MemInfoDumpProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems,
true, dumpSwapPss);
}
proto.end(token);
@@ -19027,16 +19032,16 @@
if (nativeProcs.size() > 0) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
- proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
- proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+ proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
+ proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
Debug.MemoryInfo mi = null;
for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
final ProcessCpuTracker.Stats r = nativeProcs.get(i);
final int pid = r.pid;
- final long nToken = proto.start(MemInfoProto.NATIVE_PROCESSES);
+ final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
- proto.write(MemInfoProto.ProcessMemory.PID, pid);
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.baseName);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
if (mi == null) {
mi = new Debug.MemoryInfo();
@@ -19068,8 +19073,8 @@
ProtoOutputStream proto = new ProtoOutputStream(fd);
- proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
- proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+ proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
+ proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
@@ -19134,10 +19139,10 @@
}
if (opts.dumpDetails) {
if (opts.localOnly) {
- final long aToken = proto.start(MemInfoProto.APP_PROCESSES);
- final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
- proto.write(MemInfoProto.ProcessMemory.PID, pid);
- proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.processName);
+ final long aToken = proto.start(MemInfoDumpProto.APP_PROCESSES);
+ final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.processName);
ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
proto.end(mToken);
@@ -19149,7 +19154,7 @@
thread.dumpMemInfoProto(tp.getWriteFd(),
mi, opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
opts.dumpUnreachable, innerArgs);
- proto.write(MemInfoProto.APP_PROCESSES, tp.get());
+ proto.write(MemInfoDumpProto.APP_PROCESSES, tp.get());
} finally {
tp.kill();
}
@@ -19337,13 +19342,13 @@
opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
if (!opts.oomOnly) {
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_PROCESS, "proc",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_PROCESS, "proc",
procMems, true, opts.dumpSwapPss);
}
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom",
oomMems, false, opts.dumpSwapPss);
if (!brief && !opts.oomOnly) {
- dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_CATEGORY, "cat",
+ dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_CATEGORY, "cat",
catMems, true, opts.dumpSwapPss);
}
MemInfoReader memInfo = new MemInfoReader();
@@ -19361,40 +19366,40 @@
}
}
if (!brief) {
- proto.write(MemInfoProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
- proto.write(MemInfoProto.STATUS, mLastMemoryLevel);
- proto.write(MemInfoProto.CACHED_PSS_KB, cachedPss);
- proto.write(MemInfoProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
- proto.write(MemInfoProto.FREE_KB, memInfo.getFreeSizeKb());
+ proto.write(MemInfoDumpProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
+ proto.write(MemInfoDumpProto.STATUS, mLastMemoryLevel);
+ proto.write(MemInfoDumpProto.CACHED_PSS_KB, cachedPss);
+ proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
+ proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb());
}
long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
- proto.write(MemInfoProto.USED_PSS_KB, totalPss - cachedPss);
- proto.write(MemInfoProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
- proto.write(MemInfoProto.LOST_RAM_KB, lostRAM);
+ proto.write(MemInfoDumpProto.USED_PSS_KB, totalPss - cachedPss);
+ proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
+ proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM);
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
- proto.write(MemInfoProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb());
- proto.write(MemInfoProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB,
+ proto.write(MemInfoDumpProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb());
+ proto.write(MemInfoDumpProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB,
memInfo.getSwapTotalSizeKb() - memInfo.getSwapFreeSizeKb());
- proto.write(MemInfoProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb());
+ proto.write(MemInfoDumpProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb());
}
final long[] ksm = getKsmInfo();
- proto.write(MemInfoProto.KSM_SHARING_KB, ksm[KSM_SHARING]);
- proto.write(MemInfoProto.KSM_SHARED_KB, ksm[KSM_SHARED]);
- proto.write(MemInfoProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]);
- proto.write(MemInfoProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]);
+ proto.write(MemInfoDumpProto.KSM_SHARING_KB, ksm[KSM_SHARING]);
+ proto.write(MemInfoDumpProto.KSM_SHARED_KB, ksm[KSM_SHARED]);
+ proto.write(MemInfoDumpProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]);
+ proto.write(MemInfoDumpProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]);
- proto.write(MemInfoProto.TUNING_MB, ActivityManager.staticGetMemoryClass());
- proto.write(MemInfoProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass());
- proto.write(MemInfoProto.OOM_KB,
+ proto.write(MemInfoDumpProto.TUNING_MB, ActivityManager.staticGetMemoryClass());
+ proto.write(MemInfoDumpProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass());
+ proto.write(MemInfoDumpProto.OOM_KB,
mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024);
- proto.write(MemInfoProto.RESTORE_LIMIT_KB,
+ proto.write(MemInfoDumpProto.RESTORE_LIMIT_KB,
mProcessList.getCachedRestoreThresholdKb());
- proto.write(MemInfoProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic());
- proto.write(MemInfoProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx());
+ proto.write(MemInfoDumpProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic());
+ proto.write(MemInfoDumpProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx());
}
}
@@ -22728,12 +22733,6 @@
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making top: " + app);
- } else if (app.runningRemoteAnimation) {
- adj = ProcessList.VISIBLE_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "running-remote-anim";
- procState = PROCESS_STATE_CUR_TOP;
- if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making running remote anim: " + app);
} else if (app.instr != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -22809,9 +22808,7 @@
app.adjType = "vis-activity";
if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to vis-activity: " + app);
}
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -22834,9 +22831,7 @@
app.adjType = "pause-activity";
if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to pause-activity: " + app);
}
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -25977,11 +25972,6 @@
}
}
- @Override
- public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
- ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation);
- }
-
/**
* Called after the network policy rules are updated by
* {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid}
@@ -26174,6 +26164,10 @@
return getRecentTasks().isCallerRecents(callingUid);
}
+ public boolean isRecentsComponentHomeActivity(int userId) {
+ return getRecentTasks().isRecentsComponentHomeActivity(userId);
+ }
+
@Override
public boolean isUidActive(int uid) {
synchronized (ActivityManagerService.this) {
@@ -26527,7 +26521,6 @@
throws RemoteException {
enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimations");
- definition.setCallingPid(Binder.getCallingPid());
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2251d2c..81dae39 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -51,6 +51,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.opengl.GLES10;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -82,16 +83,18 @@
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.opengles.GL;
-import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
@@ -1858,6 +1861,137 @@
}
}
+ /**
+ * Adds all supported GL extensions for a provided EGLConfig to a set by creating an EGLContext
+ * and EGLSurface and querying extensions.
+ *
+ * @param egl An EGL API object
+ * @param display An EGLDisplay to create a context and surface with
+ * @param config The EGLConfig to get the extensions for
+ * @param surfaceSize eglCreatePbufferSurface generic parameters
+ * @param contextAttribs eglCreateContext generic parameters
+ * @param glExtensions A Set<String> to add GL extensions to
+ */
+ private static void addExtensionsForConfig(
+ EGL10 egl,
+ EGLDisplay display,
+ EGLConfig config,
+ int[] surfaceSize,
+ int[] contextAttribs,
+ Set<String> glExtensions) {
+ // Create a context.
+ EGLContext context =
+ egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, contextAttribs);
+ // No-op if we can't create a context.
+ if (context == EGL10.EGL_NO_CONTEXT) {
+ return;
+ }
+
+ // Create a surface.
+ EGLSurface surface = egl.eglCreatePbufferSurface(display, config, surfaceSize);
+ if (surface == EGL10.EGL_NO_SURFACE) {
+ egl.eglDestroyContext(display, context);
+ return;
+ }
+
+ // Update the current surface and context.
+ egl.eglMakeCurrent(display, surface, surface, context);
+
+ // Get the list of extensions.
+ String extensionList = GLES10.glGetString(GLES10.GL_EXTENSIONS);
+ if (!TextUtils.isEmpty(extensionList)) {
+ // The list of extensions comes from the driver separated by spaces.
+ // Split them apart and add them into a Set for deduping purposes.
+ for (String extension : extensionList.split(" ")) {
+ glExtensions.add(extension);
+ }
+ }
+
+ // Tear down the context and surface for this config.
+ egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ egl.eglDestroySurface(display, surface);
+ egl.eglDestroyContext(display, context);
+ }
+
+
+ Set<String> getGlExtensionsFromDriver() {
+ Set<String> glExtensions = new HashSet<>();
+
+ // Get the EGL implementation.
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ if (egl == null) {
+ getErrPrintWriter().println("Warning: couldn't get EGL");
+ return glExtensions;
+ }
+
+ // Get the default display and initialize it.
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ int[] version = new int[2];
+ egl.eglInitialize(display, version);
+
+ // Call getConfigs() in order to find out how many there are.
+ int[] numConfigs = new int[1];
+ if (!egl.eglGetConfigs(display, null, 0, numConfigs)) {
+ getErrPrintWriter().println("Warning: couldn't get EGL config count");
+ return glExtensions;
+ }
+
+ // Allocate space for all configs and ask again.
+ EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+ if (!egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) {
+ getErrPrintWriter().println("Warning: couldn't get EGL configs");
+ return glExtensions;
+ }
+
+ // Allocate surface size parameters outside of the main loop to cut down
+ // on GC thrashing. 1x1 is enough since we are only using it to get at
+ // the list of extensions.
+ int[] surfaceSize =
+ new int[] {
+ EGL10.EGL_WIDTH, 1,
+ EGL10.EGL_HEIGHT, 1,
+ EGL10.EGL_NONE
+ };
+
+ // For when we need to create a GLES2.0 context.
+ final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ int[] gles2 = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
+
+ // For getting return values from eglGetConfigAttrib
+ int[] attrib = new int[1];
+
+ for (int i = 0; i < numConfigs[0]; i++) {
+ // Get caveat for this config in order to skip slow (i.e. software) configs.
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_CAVEAT, attrib);
+ if (attrib[0] == EGL10.EGL_SLOW_CONFIG) {
+ continue;
+ }
+
+ // If the config does not support pbuffers we cannot do an eglMakeCurrent
+ // on it in addExtensionsForConfig(), so skip it here. Attempting to make
+ // it current with a pbuffer will result in an EGL_BAD_MATCH error
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_SURFACE_TYPE, attrib);
+ if ((attrib[0] & EGL10.EGL_PBUFFER_BIT) == 0) {
+ continue;
+ }
+
+ final int EGL_OPENGL_ES_BIT = 0x0001;
+ final int EGL_OPENGL_ES2_BIT = 0x0004;
+ egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_RENDERABLE_TYPE, attrib);
+ if ((attrib[0] & EGL_OPENGL_ES_BIT) != 0) {
+ addExtensionsForConfig(egl, display, configs[i], surfaceSize, null, glExtensions);
+ }
+ if ((attrib[0] & EGL_OPENGL_ES2_BIT) != 0) {
+ addExtensionsForConfig(egl, display, configs[i], surfaceSize, gles2, glExtensions);
+ }
+ }
+
+ // Release all EGL resources.
+ egl.eglTerminate(display);
+
+ return glExtensions;
+ }
+
private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
PrintWriter pw, Configuration config, DisplayManager dm) {
Point stableSize = dm.getStableDisplaySize();
@@ -1906,18 +2040,24 @@
}
}
- /*
- GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
- protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
- gl.glGetString(GL10.GL_VERSION));
- String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
- for (String ext : glExtensions.split(" ")) {
- protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
+ Set<String> glExtensionsSet = getGlExtensionsFromDriver();
+ String[] glExtensions = new String[glExtensionsSet.size()];
+ glExtensions = glExtensionsSet.toArray(glExtensions);
+ Arrays.sort(glExtensions);
+ for (int i = 0; i < glExtensions.length; i++) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS,
+ glExtensions[i]);
+ }
+ if (pw != null) {
+ pw.print("opengl-extensions: "); pw.println(glExtensions[i]);
+ }
+
}
- */
PackageManager pm = mInternal.mContext.getPackageManager();
List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
+ Collections.sort(slibs, Comparator.comparing(SharedLibraryInfo::getName));
for (int i = 0; i < slibs.size(); i++) {
if (protoOutputStream != null) {
protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
@@ -1929,6 +2069,8 @@
}
FeatureInfo[] features = pm.getSystemAvailableFeatures();
+ Arrays.sort(features, (o1, o2) ->
+ (o1.name == o2.name ? 0 : (o1.name == null ? -1 : o1.name.compareTo(o2.name))));
for (int i = 0; i < features.length; i++) {
if (features[i].name != null) {
if (protoOutputStream != null) {
@@ -2811,7 +2953,7 @@
pw.println(" crash [--user <USER_ID>] <PACKAGE|PID>");
pw.println(" Induce a VM crash in the specified package or process");
pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>");
- pw.println(" Kill all processes associated with the given application.");
+ pw.println(" Kill all background processes associated with the given application.");
pw.println(" kill-all");
pw.println(" Kill all processes that are safe to kill (cached, etc).");
pw.println(" make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 978e344..5d5ed55 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -51,6 +51,7 @@
import android.util.StatsLog;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
@@ -74,8 +75,6 @@
private static final long INVALID_START_TIME = -1;
private static final int MSG_CHECK_VISIBILITY = 0;
- private static final int MSG_LOG_APP_TRANSITION = 1;
- private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 2;
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
@@ -116,13 +115,6 @@
final SomeArgs args = (SomeArgs) msg.obj;
checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
break;
- case MSG_LOG_APP_TRANSITION:
- logAppTransition(msg.arg1, msg.arg2,
- (WindowingModeTransitionInfoSnapshot) msg.obj);
- break;
- case MSG_LOG_APP_START_MEMORY_STATE_CAPTURE:
- logAppStartMemoryStateCapture((WindowingModeTransitionInfo) msg.obj);
- break;
}
}
}
@@ -141,11 +133,13 @@
private final class WindowingModeTransitionInfoSnapshot {
final private ApplicationInfo applicationInfo;
+ final private ProcessRecord processRecord;
final private String packageName;
final private String launchedActivityName;
final private String launchedActivityLaunchedFromPackage;
final private String launchedActivityLaunchToken;
final private String launchedActivityAppRecordRequiredAbi;
+ final private String processName;
final private int reason;
final private int startingWindowDelayMs;
final private int bindApplicationDelayMs;
@@ -166,6 +160,8 @@
bindApplicationDelayMs = info.bindApplicationDelayMs;
windowsDrawnDelayMs = info.windowsDrawnDelayMs;
type = getTransitionType(info);
+ processRecord = findProcessForActivity(info.launchedActivity);
+ processName = info.launchedActivity.processName;
}
}
@@ -505,15 +501,16 @@
// This will avoid any races with other operations that modify the ActivityRecord.
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info);
- mHandler.obtainMessage(MSG_LOG_APP_TRANSITION, mCurrentTransitionDeviceUptime,
- mCurrentTransitionDelayMs, infoSnapshot).sendToTarget();
+ final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
+ final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
+ BackgroundThread.getHandler().post(() -> logAppTransition(
+ currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
info.launchedActivity.info.launchToken = null;
- mHandler.obtainMessage(MSG_LOG_APP_START_MEMORY_STATE_CAPTURE, info).sendToTarget();
}
}
- // This gets called on the handler without holding the activity manager lock.
+ // This gets called on a background thread without holding the activity manager lock.
private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
WindowingModeTransitionInfoSnapshot info) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
@@ -572,6 +569,7 @@
launchToken,
packageOptimizationInfo.getCompilationReason(),
packageOptimizationInfo.getCompilationFilter());
+ logAppStartMemoryStateCapture(info);
}
private int convertAppStartTransitionType(int tronType) {
@@ -629,15 +627,14 @@
return -1;
}
- private void logAppStartMemoryStateCapture(WindowingModeTransitionInfo info) {
- final ProcessRecord processRecord = findProcessForActivity(info.launchedActivity);
- if (processRecord == null) {
+ private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
+ if (info.processRecord == null) {
if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
return;
}
- final int pid = processRecord.pid;
- final int uid = info.launchedActivity.appInfo.uid;
+ final int pid = info.processRecord.pid;
+ final int uid = info.applicationInfo.uid;
final MemoryStat memoryStat = readMemoryStatFromMemcg(uid, pid);
if (memoryStat == null) {
if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture memoryStat null");
@@ -647,8 +644,8 @@
StatsLog.write(
StatsLog.APP_START_MEMORY_STATE_CAPTURED,
uid,
- info.launchedActivity.processName,
- info.launchedActivity.info.name,
+ info.processName,
+ info.launchedActivityName,
memoryStat.pgfault,
memoryStat.pgmajfault,
memoryStat.rssInBytes,
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4987b33..728c07d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -109,6 +109,7 @@
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.servertransaction.DestroyActivityItem;
@@ -603,6 +604,8 @@
// the one where the home stack is visible since recents isn't visible yet, but the
// divider will be off. I think we should just make the initial bounds that of home
// so that the divider matches and remove this logic.
+ // TODO: This is currently only called when entering split-screen while in another
+ // task, and from the tests
final ActivityStack recentStack = display.getOrCreateStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
true /* onTop */);
@@ -2607,6 +2610,8 @@
}
try {
+ final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
+ next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -2614,15 +2619,13 @@
if (!next.finishing && N > 0) {
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Delivering results to " + next + ": " + a);
- mService.mLifecycleManager.scheduleTransaction(next.app.thread,
- next.appToken, ActivityResultItem.obtain(a));
+ transaction.addCallback(ActivityResultItem.obtain(a));
}
}
if (next.newIntents != null) {
- mService.mLifecycleManager.scheduleTransaction(next.app.thread,
- next.appToken, NewIntentItem.obtain(next.newIntents,
- false /* andPause */));
+ transaction.addCallback(NewIntentItem.obtain(next.newIntents,
+ false /* andPause */));
}
// Well the app will no longer be stopped.
@@ -2639,11 +2642,13 @@
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
- mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
+
+ transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.repProcState,
mService.isNextTransitionForward())
.setDescription(next.getLifecycleDescription(
"resumeTopActivityInnerLocked")));
+ mService.mLifecycleManager.scheduleTransaction(transaction);
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5577186..26ffe95 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -93,6 +93,7 @@
import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.proto.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -164,7 +165,6 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -2540,6 +2540,11 @@
}
}
+ void deferUpdateRecentsHomeStackBounds() {
+ deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ deferUpdateBounds(ACTIVITY_TYPE_HOME);
+ }
+
void deferUpdateBounds(int activityType) {
final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
@@ -2547,6 +2552,11 @@
}
}
+ void continueUpdateRecentsHomeStackBounds() {
+ continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ continueUpdateBounds(ACTIVITY_TYPE_HOME);
+ }
+
void continueUpdateBounds(int activityType) {
final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
@@ -2555,7 +2565,7 @@
}
void notifyAppTransitionDone() {
- continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ continueUpdateRecentsHomeStackBounds();
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
@@ -3760,12 +3770,15 @@
pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
}
}
+ pw.print(prefix); pw.print("isHomeRecentsComponent=");
+ pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
mKeyguardController.dump(pw, prefix);
mService.mLockTaskController.dump(pw, prefix);
}
- public void writeToProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
@@ -3781,6 +3794,9 @@
} else {
proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
}
+ proto.write(IS_HOME_RECENTS_COMPONENT,
+ mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ proto.end(token);
}
/**
@@ -4546,14 +4562,14 @@
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
- deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ deferUpdateRecentsHomeStackBounds();
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
activityOptions, ON_TOP);
if (task == null) {
- continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ continueUpdateRecentsHomeStackBounds();
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecents: Task " + taskId + " not found.");
@@ -4611,6 +4627,11 @@
// window manager can correctly calculate the focus window that can receive
// input keys.
moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+
+ // Immediately update the minimized docked stack mode, the upcoming animation
+ // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
+ // will do the animation to the target bounds
+ mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
}
}
mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0bf2691..1f60755 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -129,12 +129,6 @@
// When true the process will oom adj score will be set to
// ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
// of the process getting killed.
- boolean runningRemoteAnimation; // Is the process currently running a RemoteAnimation? When true
- // the process will be set to use the
- // ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost
- // performance, as well as oom adj score will be set to
- // ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
- // of the process getting killed.
boolean pendingUiClean; // Want to clean up resources from showing UI?
boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower
boolean treatLikeActivity; // Bound using BIND_TREAT_LIKE_ACTIVITY
@@ -342,10 +336,9 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (hasTopUi || hasOverlayUi || runningRemoteAnimation) {
+ if (hasTopUi || hasOverlayUi) {
pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi);
- pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
- pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
+ pw.print(" hasOverlayUi="); pw.println(hasOverlayUi);
}
if (foregroundServices || forcingToImportant != null) {
pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 2de84ab..5fd300c 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -279,6 +279,16 @@
}
/**
+ * @return whether the home app is also the active handler of recent tasks.
+ */
+ boolean isRecentsComponentHomeActivity(int userId) {
+ final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
+ .getDefaultHomeActivity(userId);
+ return defaultHomeActivity != null &&
+ defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
+ }
+
+ /**
* @return the recents component.
*/
ComponentName getRecentsComponent() {
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 0ef8bff..4b1594c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -50,7 +50,6 @@
private final WindowManagerService mWindowManager;
private final UserController mUserController;
private final Handler mHandler;
- private final int mCallingPid;
private final Runnable mCancelAnimationRunnable;
@@ -59,14 +58,13 @@
RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
- UserController userController, int callingPid) {
+ UserController userController) {
mService = am;
mStackSupervisor = stackSupervisor;
mActivityStartController = activityStartController;
mHandler = new Handler(mStackSupervisor.mLooper);
mWindowManager = wm;
mUserController = userController;
- mCallingPid = callingPid;
mCancelAnimationRunnable = () -> {
// The caller has not finished the animation in a predefined amount of time, so
@@ -96,10 +94,9 @@
}
}
- mService.setRunningRemoteAnimation(mCallingPid, true);
-
mWindowManager.deferSurfaceLayout();
try {
+
final ActivityDisplay display;
if (hasExistingHomeActivity) {
// Move the home activity into place for the animation if it is not already top most
@@ -137,6 +134,7 @@
// Fetch all the surface controls and pass them to the client to get the animation
// started
+ mWindowManager.cancelRecentsAnimation();
mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
display.mDisplayId);
@@ -155,8 +153,6 @@
synchronized (mService) {
if (mWindowManager.getRecentsAnimationController() == null) return;
- mService.setRunningRemoteAnimation(mCallingPid, false);
-
mWindowManager.inSurfaceTransaction(() -> {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
"RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index ac6f01f..d08111e 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -121,16 +121,10 @@
if (mOriginalOptions != null) {
checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
mOriginalCallingPid, mOriginalCallingUid);
- if (mOriginalOptions.getRemoteAnimationAdapter() != null) {
- mOriginalOptions.getRemoteAnimationAdapter().setCallingPid(mOriginalCallingPid);
- }
}
if (mCallerOptions != null) {
checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
mRealCallingPid, mRealCallingUid);
- if (mCallerOptions.getRemoteAnimationAdapter() != null) {
- mCallerOptions.getRemoteAnimationAdapter().setCallingPid(mRealCallingPid);
- }
}
return mergeActivityOptions(mOriginalOptions, mCallerOptions);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6134d05..af1ab83 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1735,6 +1735,20 @@
return mInjector.getUserManager().getUserIds();
}
+ /**
+ * If {@code userId} is {@link UserHandle#USER_ALL}, then return an array with all running user
+ * IDs. Otherwise return an array whose only element is the given user id.
+ *
+ * It doesn't handle other special user IDs such as {@link UserHandle#USER_CURRENT}.
+ */
+ int[] expandUserId(int userId) {
+ if (userId != UserHandle.USER_ALL) {
+ return new int[] {userId};
+ } else {
+ return getUsers();
+ }
+ }
+
boolean exists(int userId) {
return mInjector.getUserManager().exists(userId);
}
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index d32db7e..9d34a80 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -24,7 +24,7 @@
import android.util.proto.ProtoUtils;
import com.android.server.LocalServices;
-import com.android.server.am.proto.ProcessesProto.VrControllerProto;
+import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.VrControllerProto;
import com.android.server.vr.VrManagerInternal;
/**
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
index e5d564d..f44afe4 100644
--- a/services/core/java/com/android/server/backup/BackupUtils.java
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -18,9 +18,12 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -30,9 +33,10 @@
public class BackupUtils {
private static final String TAG = "BackupUtils";
- private static final boolean DEBUG = false; // STOPSHIP if true
+ private static final boolean DEBUG = false;
- public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
+ public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target,
+ PackageManagerInternal pmi) {
if (target == null) {
return false;
}
@@ -47,48 +51,54 @@
return true;
}
- // Allow unsigned apps, but not signed on one device and unsigned on the other
- // !!! TODO: is this the right policy?
- Signature[] deviceSigs = target.signatures;
- if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
- + " device=" + deviceSigs);
- if ((storedSigHashes == null || storedSigHashes.size() == 0)
- && (deviceSigs == null || deviceSigs.length == 0)) {
- return true;
- }
- if (storedSigHashes == null || deviceSigs == null) {
+ // Don't allow unsigned apps on either end
+ if (ArrayUtils.isEmpty(storedSigHashes)) {
return false;
}
- // !!! TODO: this demands that every stored signature match one
- // that is present on device, and does not demand the converse.
- // Is this this right policy?
- final int nStored = storedSigHashes.size();
- final int nDevice = deviceSigs.length;
-
- // hash each on-device signature
- ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
- for (int i = 0; i < nDevice; i++) {
- deviceHashes.add(hashSignature(deviceSigs[i]));
+ Signature[][] deviceHistorySigs = target.signingCertificateHistory;
+ if (ArrayUtils.isEmpty(deviceHistorySigs)) {
+ Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+ " PackageManager#GET_SIGNING_CERTIFICATES was not specified");
+ return false;
}
- // now ensure that each stored sig (hash) matches an on-device sig (hash)
- for (int n = 0; n < nStored; n++) {
- boolean match = false;
- final byte[] storedHash = storedSigHashes.get(n);
- for (int i = 0; i < nDevice; i++) {
- if (Arrays.equals(storedHash, deviceHashes.get(i))) {
- match = true;
- break;
+ if (DEBUG) {
+ Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
+ + " device=" + deviceHistorySigs);
+ }
+
+ final int nStored = storedSigHashes.size();
+ if (nStored == 1) {
+ // if the app is only signed with one sig, it's possible it has rotated its key
+ // the checks with signing history are delegated to PackageManager
+ // TODO: address the case that app has declared restoreAnyVersion and is restoring
+ // from higher version to lower after having rotated the key (i.e. higher version has
+ // different sig than lower version that we want to restore to)
+ return pmi.isDataRestoreSafe(storedSigHashes.get(0), target.packageName);
+ } else {
+ // the app couldn't have rotated keys, since it was signed with multiple sigs - do
+ // a comprehensive 1-to-1 signatures check
+ // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
+ ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
+ int nDevice = deviceHashes.size();
+
+ // ensure that each stored sig matches an on-device sig
+ for (int i = 0; i < nStored; i++) {
+ boolean match = false;
+ for (int j = 0; j < nDevice; j++) {
+ if (Arrays.equals(storedSigHashes.get(i), deviceHashes.get(j))) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
}
}
- // match is false when no on-device sig matched one of the stored ones
- if (!match) {
- return false;
- }
+ // we have found a match for all stored sigs
+ return true;
}
-
- return true;
}
public static byte[] hashSignature(byte[] signature) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e2aee88..cb53521 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -67,7 +67,7 @@
private static final int MSG_UPDATE_AMBIENT_LUX = 1;
private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
- private static final int MSG_RESET_SHORT_TERM_MODEL = 3;
+ private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -158,9 +158,6 @@
// A ring buffer containing all of the recent ambient light sensor readings.
private AmbientLightRingBuffer mAmbientLightRingBuffer;
- // A ring buffer containing the light sensor readings for the initial horizon period.
- private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
-
// The handler
private AutomaticBrightnessHandler mHandler;
@@ -194,6 +191,14 @@
private int mBrightnessAdjustmentSampleOldBrightness;
private float mBrightnessAdjustmentSampleOldGamma;
+ // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
+ // user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
+ // The anchor determines what were the light levels when the user has set her preference, and
+ // we use a relative threshold to determine when to revert to the OEM curve.
+ private boolean mShortTermModelValid;
+ private float mShortTermModelAnchor;
+ private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -218,12 +223,12 @@
mWeightingIntercept = ambientLightHorizon;
mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
mDynamicHysteresis = dynamicHysteresis;
+ mShortTermModelValid = true;
+ mShortTermModelAnchor = -1;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer =
new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
- mInitialHorizonAmbientLightRingBuffer =
- new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
@@ -284,13 +289,13 @@
final int oldPolicy = mDisplayPolicy;
mDisplayPolicy = policy;
if (DEBUG) {
- Slog.d(TAG, "Display policy transitioning from " + mDisplayPolicy + " to " + policy);
+ Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
- mHandler.sendEmptyMessageDelayed(MSG_RESET_SHORT_TERM_MODEL,
+ mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
SHORT_TERM_MODEL_TIMEOUT_MILLIS);
} else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
- mHandler.removeMessages(MSG_RESET_SHORT_TERM_MODEL);
+ mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
}
return true;
}
@@ -308,6 +313,11 @@
return false;
}
mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
+ mShortTermModelValid = true;
+ mShortTermModelAnchor = mAmbientLux;
+ if (DEBUG) {
+ Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
+ }
// Reset the brightness adjustment so that the next time we're queried for brightness we
// return the value the user set.
mScreenAutoBrightnessAdjustment = 0.0f;
@@ -316,6 +326,15 @@
private void resetShortTermModel() {
mBrightnessMapper.clearUserDataPoints();
+ mShortTermModelValid = true;
+ mShortTermModelAnchor = -1;
+ }
+
+ private void invalidateShortTermModel() {
+ if (DEBUG) {
+ Slog.d(TAG, "ShortTermModel: invalidate user data");
+ }
+ mShortTermModelValid = false;
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
@@ -345,14 +364,13 @@
pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
pw.println(" mRecentLightSamples=" + mRecentLightSamples);
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
- pw.println(" mInitialHorizonAmbientLightRingBuffer=" +
- mInitialHorizonAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma="
+ mScreenAutoBrightnessAdjustmentMaxGamma);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
pw.println(" mDisplayPolicy=" + mDisplayPolicy);
+ pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
pw.println();
mBrightnessMapper.dump(pw);
@@ -368,17 +386,14 @@
mCurrentLightSensorRate * 1000, mHandler);
return true;
}
- } else {
- if (mLightSensorEnabled) {
- mLightSensorEnabled = false;
- mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
- mRecentLightSamples = 0;
- mAmbientLightRingBuffer.clear();
- mInitialHorizonAmbientLightRingBuffer.clear();
- mCurrentLightSensorRate = -1;
- mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
- mSensorManager.unregisterListener(mLightSensorListener);
- }
+ } else if (mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
+ mRecentLightSamples = 0;
+ mAmbientLightRingBuffer.clear();
+ mCurrentLightSensorRate = -1;
+ mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
}
@@ -397,11 +412,6 @@
private void applyLightSensorMeasurement(long time, float lux) {
mRecentLightSamples++;
- // Store all of the light measurements for the intial horizon period. This is to help
- // diagnose dim wake ups and slow responses in b/27951906.
- if (time <= mLightSensorEnableTime + mAmbientLightHorizon) {
- mInitialHorizonAmbientLightRingBuffer.push(time, lux);
- }
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
mAmbientLightRingBuffer.push(time, lux);
@@ -414,8 +424,9 @@
// if the light sensor rate changed, update the sensor listener
if (lightSensorRate != mCurrentLightSensorRate) {
if (DEBUG) {
- Slog.d(TAG, "adjustLightSensorRate: previousRate=" + mCurrentLightSensorRate
- + ", currentRate=" + lightSensorRate);
+ Slog.d(TAG, "adjustLightSensorRate: " +
+ "previousRate=" + mCurrentLightSensorRate + ", " +
+ "currentRate=" + lightSensorRate);
}
mCurrentLightSensorRate = lightSensorRate;
mSensorManager.unregisterListener(mLightSensorListener);
@@ -437,12 +448,29 @@
Slog.d(TAG, "setAmbientLux(" + lux + ")");
}
if (lux < 0) {
- Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0.");
+ Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
lux = 0;
}
mAmbientLux = lux;
mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux);
mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux);
+
+ // If the short term model was invalidated and the change is drastic enough, reset it.
+ if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
+ final float minAmbientLux =
+ mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
+ final float maxAmbientLux =
+ mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
+ if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
+ Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
+ minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
+ mShortTermModelValid = true;
+ } else {
+ Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + mAmbientLux +
+ "(" + minAmbientLux + ", " + maxAmbientLux + ")");
+ resetShortTermModel();
+ }
+ }
}
private float calculateAmbientLux(long now, long horizon) {
@@ -467,9 +495,8 @@
}
if (DEBUG) {
Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=("
- + mAmbientLightRingBuffer.getTime(endIndex) + ", "
- + mAmbientLightRingBuffer.getLux(endIndex)
- + ")");
+ + mAmbientLightRingBuffer.getTime(endIndex) + ", "
+ + mAmbientLightRingBuffer.getLux(endIndex) + ")");
}
float sum = 0;
float totalWeight = 0;
@@ -485,17 +512,18 @@
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
if (DEBUG) {
- Slog.d(TAG, "calculateAmbientLux: [" +
- (startTime) + ", " +
- (endTime) + "]: lux=" + lux + ", weight=" + weight);
+ Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
+ "lux=" + lux + ", " +
+ "weight=" + weight);
}
totalWeight += weight;
- sum += mAmbientLightRingBuffer.getLux(i) * weight;
+ sum += lux * weight;
endTime = startTime;
}
if (DEBUG) {
- Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
- ", newAmbientLux=" + (sum / totalWeight));
+ Slog.d(TAG, "calculateAmbientLux: " +
+ "totalWeight=" + totalWeight + ", " +
+ "newAmbientLux=" + (sum / totalWeight));
}
return sum / totalWeight;
}
@@ -548,9 +576,9 @@
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
- + "time=" + time
- + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
+ Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
+ "time=" + time + ", " +
+ "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
}
mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
timeWhenSensorWarmedUp);
@@ -559,9 +587,9 @@
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
mAmbientLuxValid = true;
if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Initializing: "
- + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
- + ", mAmbientLux=" + mAmbientLux);
+ Slog.d(TAG, "updateAmbientLux: Initializing: " +
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
+ "mAmbientLux=" + mAmbientLux);
}
updateAutoBrightness(true);
}
@@ -579,17 +607,20 @@
float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
- if (slowAmbientLux >= mBrighteningLuxThreshold &&
- fastAmbientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
- || slowAmbientLux <= mDarkeningLuxThreshold
- && fastAmbientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
+ if ((slowAmbientLux >= mBrighteningLuxThreshold &&
+ fastAmbientLux >= mBrighteningLuxThreshold &&
+ nextBrightenTransition <= time)
+ ||
+ (slowAmbientLux <= mDarkeningLuxThreshold &&
+ fastAmbientLux <= mDarkeningLuxThreshold &&
+ nextDarkenTransition <= time)) {
setAmbientLux(fastAmbientLux);
if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: "
- + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
- + ", mAmbientLux=" + mAmbientLux);
+ Slog.d(TAG, "updateAmbientLux: " +
+ ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " +
+ "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold + ", " +
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
+ "mAmbientLux=" + mAmbientLux);
}
updateAutoBrightness(true);
nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
@@ -605,8 +636,8 @@
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
- + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
+ Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
+ nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
}
@@ -633,8 +664,10 @@
final float in = value;
value = MathUtils.pow(value, gamma);
if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
- + ", in=" + in + ", out=" + value);
+ Slog.d(TAG, "updateAutoBrightness: " +
+ "gamma=" + gamma + ", " +
+ "in=" + in + ", " +
+ "out=" + value);
}
}
@@ -642,9 +675,9 @@
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
if (mScreenAutoBrightness != newScreenAutoBrightness) {
if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
- + mScreenAutoBrightness + ", newScreenAutoBrightness="
- + newScreenAutoBrightness);
+ Slog.d(TAG, "updateAutoBrightness: " +
+ "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
+ "newScreenAutoBrightness=" + newScreenAutoBrightness);
}
mScreenAutoBrightness = newScreenAutoBrightness;
@@ -687,12 +720,12 @@
mBrightnessAdjustmentSamplePending = false;
if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
if (DEBUG) {
- Slog.d(TAG, "Auto-brightness adjustment changed by user: "
- + "adj=" + mScreenAutoBrightnessAdjustment
- + ", lux=" + mAmbientLux
- + ", brightness=" + mScreenAutoBrightness
- + ", gamma=" + mLastScreenAutoBrightnessGamma
- + ", ring=" + mAmbientLightRingBuffer);
+ Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
+ "adj=" + mScreenAutoBrightnessAdjustment + ", " +
+ "lux=" + mAmbientLux + ", " +
+ "brightness=" + mScreenAutoBrightness + ", " +
+ "gamma=" + mLastScreenAutoBrightnessGamma + ", " +
+ "ring=" + mAmbientLightRingBuffer);
}
EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
@@ -724,8 +757,8 @@
collectBrightnessAdjustmentSample();
break;
- case MSG_RESET_SHORT_TERM_MODEL:
- resetShortTermModel();
+ case MSG_INVALIDATE_SHORT_TERM_MODEL:
+ invalidateShortTermModel();
break;
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 524de91..df60c66 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -70,6 +70,8 @@
import java.util.ArrayList;
import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -225,15 +227,24 @@
* @return List of recent {@link BrightnessChangeEvent}s
*/
public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
- // TODO include apps from any managed profiles in the brightness information.
BrightnessChangeEvent[] events;
synchronized (mEventsLock) {
events = mEvents.toArray();
}
+ int[] profiles = mInjector.getProfileIds(mUserManager, userId);
+ Map<Integer, Boolean> toRedact = new HashMap<>();
+ for (int i = 0; i < profiles.length; ++i) {
+ int profileId = profiles[i];
+ // Include slider interactions when a managed profile app is in the
+ // foreground but always redact the package name.
+ boolean redact = (!includePackage) || profileId != userId;
+ toRedact.put(profiles[i], redact);
+ }
ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
for (int i = 0; i < events.length; ++i) {
- if (events[i].userId == userId) {
- if (includePackage) {
+ Boolean redact = toRedact.get(events[i].userId);
+ if (redact != null) {
+ if (!redact) {
out.add(events[i]);
} else {
BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
@@ -817,6 +828,14 @@
return userManager.getUserHandle(userSerialNumber);
}
+ public int[] getProfileIds(UserManager userManager, int userId) {
+ if (userManager != null) {
+ return userManager.getProfileIds(userId, false);
+ } else {
+ return new int[]{userId};
+ }
+ }
+
public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
return ActivityManager.getService().getFocusedStackInfo();
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5267f54..729ac0c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -459,7 +459,7 @@
private final PendingIntent mWakeupIntent;
private final PendingIntent mTimeoutIntent;
- private final IAppOpsService mAppOpsService;
+ private final AppOpsManager mAppOps;
private final IBatteryStats mBatteryStats;
// Current list of underlying location clients.
@@ -782,8 +782,7 @@
mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// App ops service to keep track of who is accessing the GPS
- mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
- Context.APP_OPS_SERVICE));
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
// Battery statistics service to be notified when GPS turns on or off
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -1490,26 +1489,16 @@
if (newChains != null) {
for (int i = 0; i < newChains.size(); ++i) {
final WorkChain newChain = newChains.get(i);
- try {
- mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, newChain.getAttributionUid(),
- newChain.getAttributionTag());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
+ newChain.getAttributionTag());
}
}
if (goneChains != null) {
for (int i = 0; i < goneChains.size(); i++) {
final WorkChain goneChain = goneChains.get(i);
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
- goneChain.getAttributionTag());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
+ goneChain.getAttributionTag());
}
}
@@ -1525,24 +1514,15 @@
// Update sources that were not previously tracked.
if (newWork != null) {
for (int i = 0; i < newWork.size(); i++) {
- try {
- mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, newWork.get(i), newWork.getName(i));
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.startOpNoThrow(AppOpsManager.OP_GPS,
+ newWork.get(i), newWork.getName(i));
}
}
// Update sources that are no longer tracked.
if (goneWork != null) {
for (int i = 0; i < goneWork.size(); i++) {
- try {
- mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException", e);
- }
+ mAppOps.finishOp(AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index da0b0d0..1e0703a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -43,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
@@ -446,12 +447,14 @@
"Failed decode the certificate path");
}
- // TODO: Validate the cert path according to the root of trust
-
- if (certPath.getCertificates().isEmpty()) {
- throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
- "The given CertPath is empty");
+ try {
+ CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, certPath);
+ } catch (CertValidationException e) {
+ Log.e(TAG, "Failed to validate the given cert path", e);
+ // TODO: Change this to ERROR_INVALID_CERTIFICATE once ag/3666620 is submitted
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage());
}
+
byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
if (verifierPublicKey == null) {
Log.e(TAG, "Failed to encode verifierPublicKey");
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 09ec5ad..6e08949 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -40,6 +40,7 @@
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
@@ -292,6 +293,42 @@
return certPath;
}
+ /**
+ * Validates a given {@code CertPath} against the trusted root certificate.
+ *
+ * @param trustedRoot the trusted root certificate
+ * @param certPath the certificate path to be validated
+ * @throws CertValidationException if the given certificate path is invalid, e.g., is expired,
+ * or does not have a valid signature
+ */
+ public static void validateCertPath(X509Certificate trustedRoot, CertPath certPath)
+ throws CertValidationException {
+ validateCertPath(/*validationDate=*/ null, trustedRoot, certPath);
+ }
+
+ /**
+ * Validates a given {@code CertPath} against a given {@code validationDate}. If the given
+ * validation date is null, the current date will be used.
+ */
+ @VisibleForTesting
+ static void validateCertPath(@Nullable Date validationDate, X509Certificate trustedRoot,
+ CertPath certPath) throws CertValidationException {
+ if (certPath.getCertificates().isEmpty()) {
+ throw new CertValidationException("The given certificate path is empty");
+ }
+ if (!(certPath.getCertificates().get(0) instanceof X509Certificate)) {
+ throw new CertValidationException(
+ "The given certificate path does not contain X509 certificates");
+ }
+
+ List<X509Certificate> certificates = (List<X509Certificate>) certPath.getCertificates();
+ X509Certificate leafCert = certificates.get(0);
+ List<X509Certificate> intermediateCerts =
+ certificates.subList(/*fromIndex=*/ 1, certificates.size());
+
+ validateCert(validationDate, trustedRoot, intermediateCerts, leafCert);
+ }
+
@VisibleForTesting
static CertPath buildCertPath(PKIXParameters pkixParams) throws CertValidationException {
CertPathBuilder certPathBuilder;
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
index 6921ccd..f38b353 100644
--- a/services/core/java/com/android/server/media/MediaUpdateService.java
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -16,27 +16,21 @@
package com.android.server.media;
-import android.content.Context;
-import android.content.Intent;
-import android.media.IMediaResourceMonitor;
-import android.os.Binder;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.Slog;
-import com.android.server.SystemService;
-
import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.media.IMediaExtractorUpdateService;
import android.os.IBinder;
+import android.os.Handler;
import android.os.PatternMatcher;
import android.os.ServiceManager;
-import android.media.IMediaExtractorUpdateService;
-
-import java.lang.Exception;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
/** This class provides a system service that manages media framework updates. */
public class MediaUpdateService extends SystemService {
@@ -46,9 +40,11 @@
private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
private IMediaExtractorUpdateService mMediaExtractorUpdateService;
+ final Handler mHandler;
public MediaUpdateService(Context context) {
super(context);
+ mHandler = new Handler();
}
@Override
@@ -77,7 +73,12 @@
}
if (binder != null) {
mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
- packageStateChanged();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ packageStateChanged();
+ }
+ });
} else {
Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 256fb42..5cf7903 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -176,6 +176,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
import android.content.pm.PackageParser.ServiceIntentInfo;
+import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
@@ -6691,7 +6692,9 @@
return result;
}
final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
- if (ps == null) {
+ if (ps == null
+ || ps.getUserState().get(userId) == null
+ || !ps.getUserState().get(userId).isEnabled(mInstantAppInstallerActivity, 0)) {
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
@@ -23320,6 +23323,39 @@
}
@Override
+ public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
+ SigningDetails sd = getSigningDetails(packageName);
+ if (sd == null) {
+ return false;
+ }
+ return sd.hasSha256Certificate(restoringFromSigHash,
+ SigningDetails.CertCapabilities.INSTALLED_DATA);
+ }
+
+ @Override
+ public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) {
+ SigningDetails sd = getSigningDetails(packageName);
+ if (sd == null) {
+ return false;
+ }
+ return sd.hasCertificate(restoringFromSig,
+ SigningDetails.CertCapabilities.INSTALLED_DATA);
+ }
+
+ private SigningDetails getSigningDetails(String packageName) {
+ synchronized (mPackages) {
+ if (packageName == null) {
+ return null;
+ }
+ PackageParser.Package p = mPackages.get(packageName);
+ if (p == null) {
+ return null;
+ }
+ return p.mSigningDetails;
+ }
+ }
+
+ @Override
public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
}
@@ -23556,6 +23592,11 @@
}
@Override
+ public ComponentName getDefaultHomeActivity(int userId) {
+ return PackageManagerService.this.getDefaultHomeActivity(userId);
+ }
+
+ @Override
public void setDeviceAndProfileOwnerPackages(
int deviceOwnerUserId, String deviceOwnerPackage,
SparseArray<String> profileOwnerPackages) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 3d37229..f5edae0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -18,10 +18,12 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.backup.BackupUtils;
import libcore.util.HexEncoding;
@@ -136,7 +138,8 @@
//@DisabledReason
public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) {
- if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) {
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage, pmi)) {
Slog.w(TAG, "Can't restore: Package signature mismatch");
return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 076f81f..ca6f53a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3125,7 +3125,8 @@
try {
return mIPackageManager.getPackageInfo(
packageName, PACKAGE_MATCH_FLAGS
- | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
+ | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0),
+ userId);
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 41570c4..b5d8326 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -565,21 +565,21 @@
break;
case UserManager.DISALLOW_AMBIENT_DISPLAY:
if (newValue) {
- android.provider.Settings.Secure.putString(
+ android.provider.Settings.Secure.putIntForUser(
context.getContentResolver(),
- Settings.Secure.DOZE_ENABLED, "0");
- android.provider.Settings.Secure.putString(
+ Settings.Secure.DOZE_ENABLED, 0, userId);
+ android.provider.Settings.Secure.putIntForUser(
context.getContentResolver(),
- Settings.Secure.DOZE_ALWAYS_ON, "0");
- android.provider.Settings.Secure.putString(
+ Settings.Secure.DOZE_ALWAYS_ON, 0, userId);
+ android.provider.Settings.Secure.putIntForUser(
context.getContentResolver(),
- Settings.Secure.DOZE_PULSE_ON_PICK_UP, "0");
- android.provider.Settings.Secure.putString(
+ Settings.Secure.DOZE_PULSE_ON_PICK_UP, 0, userId);
+ android.provider.Settings.Secure.putIntForUser(
context.getContentResolver(),
- Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, "0");
- android.provider.Settings.Secure.putString(
+ Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, 0, userId);
+ android.provider.Settings.Secure.putIntForUser(
context.getContentResolver(),
- Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, "0");
+ Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, 0, userId);
}
break;
case UserManager.DISALLOW_CONFIG_LOCATION:
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7efc9876..eaa17ca 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -485,7 +485,7 @@
boolean mSafeMode;
private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
WindowState mStatusBar = null;
- int mStatusBarHeight;
+ private final int[] mStatusBarHeightForRotation = new int[4];
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
@@ -2768,8 +2768,12 @@
Context uiContext = getSystemUiContext();
final Resources res = uiContext.getResources();
- mStatusBarHeight =
- res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mStatusBarHeightForRotation[mPortraitRotation] =
+ mStatusBarHeightForRotation[mUpsideDownRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ mStatusBarHeightForRotation[mLandscapeRotation] =
+ mStatusBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
// Height of the navigation bar when presented horizontally at bottom
mNavigationBarHeightForRotationDefault[mPortraitRotation] =
@@ -2884,11 +2888,11 @@
// of the screen.
// TODO(multi-display): Support status bars on secondary displays.
if (displayId == DEFAULT_DISPLAY) {
- int statusBarHeight = mStatusBarHeight;
+ int statusBarHeight = mStatusBarHeightForRotation[rotation];
if (displayCutout != null) {
// If there is a cutout, it may already have accounted for some part of the status
// bar height.
- statusBarHeight = Math.max(0, mStatusBarHeight - displayCutout.getSafeInsetTop());
+ statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
}
return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId,
displayCutout) - statusBarHeight;
@@ -4649,7 +4653,8 @@
displayFrames.mDisplayCutout);
// For layout, the status bar is always at the top with our fixed height.
- displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
+ displayFrames.mStable.top = displayFrames.mUnrestricted.top
+ + mStatusBarHeightForRotation[displayFrames.mRotation];
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
@@ -5642,9 +5647,7 @@
final int fl = PolicyControl.getWindowFlags(null,
mTopFullscreenOpaqueWindowState.getAttrs());
if (localLOGV) {
- Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
- + " shown position: "
- + mTopFullscreenOpaqueWindowState.getShownPositionLw());
+ Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
}
@@ -6938,7 +6941,7 @@
// Navigation bar and status bar.
getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
- outInsets.top = Math.max(outInsets.top, mStatusBarHeight);
+ outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
}
@Override
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index bf0c3da..a07f5eb 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -232,14 +232,6 @@
public Rect getFrameLw();
/**
- * Retrieve the current position of the window that is actually shown.
- * Must be called with the window manager lock held.
- *
- * @return Point The point holding the shown window position.
- */
- public Point getShownPositionLw();
-
- /**
* Retrieve the frame of the display that this window was last
* laid out in. Must be called with the
* window manager lock held.
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 08dc97e..16336b3 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -68,6 +68,7 @@
private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
+ private static final String KEY_AOD_DISABLED = "aod_disabled";
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
@@ -200,11 +201,17 @@
private boolean mForceBackgroundCheck;
/**
- * Weather to show non-essential sensors (e.g. edge sensors) or not.
+ * Whether to show non-essential sensors (e.g. edge sensors) or not.
*/
@GuardedBy("mLock")
private boolean mOptionalSensorsDisabled;
+ /**
+ * Whether AOD is enabled or not.
+ */
+ @GuardedBy("mLock")
+ private boolean mAodDisabled;
+
@GuardedBy("mLock")
private Context mContext;
@@ -339,6 +346,7 @@
mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
+ mAodDisabled = parser.getBoolean(KEY_AOD_DISABLED, true);
// Get default value from Settings.Secure
final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
@@ -375,6 +383,7 @@
if (mLaunchBoostDisabled) sb.append("l");
if (mOptionalSensorsDisabled) sb.append("S");
+ if (mAodDisabled) sb.append("o");
sb.append(mGpsMode);
@@ -437,6 +446,9 @@
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
+ case ServiceType.AOD:
+ return builder.setBatterySaverEnabled(mAodDisabled)
+ .build();
default:
return builder.setBatterySaverEnabled(realMode)
.build();
@@ -491,6 +503,7 @@
pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
pw.println(" " + KEY_FORCE_BACKGROUND_CHECK + "=" + mForceBackgroundCheck);
pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
+ pw.println(" " + KEY_AOD_DISABLED + "=" + mAodDisabled);
pw.println();
pw.print(" Interactive File values:\n");
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3072f21..b729b6a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -91,7 +91,7 @@
private final Context mContext;
private final IBatteryStats mBatteryStats;
- private final IAppOpsService mAppOps;
+ private final AppOpsManager mAppOps;
private final SuspendBlocker mSuspendBlocker;
private final WindowManagerPolicy mPolicy;
private final ActivityManagerInternal mActivityManagerInternal;
@@ -134,11 +134,10 @@
private boolean mUserActivityPending;
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
- IAppOpsService appOps, SuspendBlocker suspendBlocker,
- WindowManagerPolicy policy) {
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
mContext = context;
mBatteryStats = batteryStats;
- mAppOps = appOps;
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -194,8 +193,7 @@
mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
monitorType, unimportantForLogging);
// XXX need to deal with disabled operations.
- mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
- AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -295,8 +293,7 @@
} else {
mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
historyTag, monitorType);
- mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
- AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -539,12 +536,11 @@
try {
mBatteryStats.noteWakeUp(reason, reasonUid);
if (opPackageName != null) {
- mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
+ mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
}
} catch (RemoteException ex) {
// Ignore
}
-
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d67acc4..f77b0ee 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -759,8 +759,7 @@
// with the animations and other critical functions of the power manager.
mBatteryStats = BatteryStatsService.getService();
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
- mPolicy);
+ createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 84c885e..c2cc7c9 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1717,6 +1717,10 @@
frame.set(win.mFrame);
} else if (win.isLetterboxedAppWindow()) {
frame.set(getTask().getBounds());
+ } else if (win.isDockedResizing()) {
+ // If we are animating while docked resizing, then use the stack bounds as the
+ // animation target (which will be different than the task bounds)
+ frame.set(getTask().getParent().getBounds());
} else {
frame.set(win.mContainingFrame);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 19c634a..332bdb7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -747,7 +747,14 @@
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+ // We use this as our arbitrary surface size for buffer-less parents
+ // that don't impose cropping on their children. It may need to be larger
+ // than the display size because fullscreen windows can be shifted offscreen
+ // due to surfaceInsets. 2 times the largest display dimension feels like an
+ // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
+ // layers the ability to match their parent sizes and be able to skip
+ // such arbitrary size settings.
+ mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
.setSize(mSurfaceSize, mSurfaceSize)
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 46c59c5..1f1efc4 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -620,7 +620,12 @@
if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
&& appTransition != TRANSIT_NONE &&
!AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
- mService.showRecentApps();
+ if (mService.mAmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
+ // When the home activity is the recents component and we are already minimized,
+ // then there is nothing to do here since home is already visible
+ } else {
+ mService.showRecentApps();
+ }
}
}
@@ -641,7 +646,7 @@
return mMinimizedDock;
}
- private void checkMinimizeChanged(boolean animate) {
+ void checkMinimizeChanged(boolean animate) {
if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) {
return;
}
@@ -693,7 +698,7 @@
final boolean imeChanged = clearImeAdjustAnimation();
boolean minimizedChange = false;
if (isHomeStackResizable()) {
- notifyDockedStackMinimizedChanged(minimizedDock, true /* animate */,
+ notifyDockedStackMinimizedChanged(minimizedDock, animate,
true /* isHomeStackResizable */);
minimizedChange = true;
} else {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 31b5c69..44ef247 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -256,18 +256,20 @@
void cancelAnimation() {
if (DEBUG) Log.d(TAG, "cancelAnimation()");
- if (mCanceled) {
- // We've already canceled the animation
- return;
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ // We've already canceled the animation
+ return;
+ }
+ mCanceled = true;
+ try {
+ mRunner.onAnimationCanceled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation", e);
+ }
}
- mCanceled = true;
- try {
- mRunner.onAnimationCanceled();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel recents animation", e);
- }
-
// Clean up and return to the previous app
+ // Don't hold the WM lock here as it calls back to AM/RecentsAnimation
mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index e4bb043..ed6e606 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -103,7 +103,6 @@
onAnimationFinished();
}
});
- sendRunningRemoteAnimation(true);
}
private RemoteAnimationTarget[] createAnimations() {
@@ -132,7 +131,6 @@
mService.closeSurfaceTransaction("RemoteAnimationController#finished");
}
}
- sendRunningRemoteAnimation(false);
}
private void invokeAnimationCancelled() {
@@ -150,14 +148,6 @@
}
}
- private void sendRunningRemoteAnimation(boolean running) {
- final int pid = mRemoteAnimationAdapter.getCallingPid();
- if (pid == 0) {
- throw new RuntimeException("Calling pid of remote animation was null");
- }
- mService.sendSetRunningRemoteAnimation(pid, running);
- }
-
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
RemoteAnimationController mOuter;
@@ -261,7 +251,6 @@
mHandler.removeCallbacks(mTimeoutRunnable);
releaseFinishedCallback();
invokeAnimationCancelled();
- sendRunningRemoteAnimation(false);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6356a35..36d331d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -29,9 +29,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -50,9 +48,6 @@
import java.util.List;
import java.util.function.Consumer;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.OP_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -68,8 +63,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -427,12 +420,7 @@
void updateAppOpsState() {
forAllWindows((w) -> {
- if (w.mAppOp == OP_NONE) {
- return;
- }
- final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(),
- w.getOwningPackage());
- w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
+ w.updateAppOpsState();
}, false /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ba08fcd2..b5d00a7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -751,8 +751,11 @@
int getStackOutset() {
if (inPinnedWindowingMode()) {
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- return mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
- displayMetrics);
+
+ // We multiply by two to match the client logic for converting view elevation
+ // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
+ return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ displayMetrics) * 2);
}
return 0;
}
@@ -824,6 +827,7 @@
}
updateDisplayInfo(bounds);
+ updateSurfaceBounds();
}
/**
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a7d51f1..2873b6d 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -275,6 +275,8 @@
}
boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
+ int xOffset = 0;
+ int yOffset = 0;
boolean rawChanged = false;
// Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
// match the behavior of most Launchers
@@ -286,11 +288,8 @@
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
}
- boolean changed = wallpaperWin.mXOffset != offset;
- if (changed) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
- wallpaperWin.mXOffset = offset;
- }
+ xOffset = offset;
+
if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
wallpaperWin.mWallpaperX = wpx;
wallpaperWin.mWallpaperXStep = wpxs;
@@ -304,17 +303,16 @@
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
}
- if (wallpaperWin.mYOffset != offset) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
- changed = true;
- wallpaperWin.mYOffset = offset;
- }
+ yOffset = offset;
+
if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
wallpaperWin.mWallpaperY = wpy;
wallpaperWin.mWallpaperYStep = wpys;
rawChanged = true;
}
+ boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset);
+
if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
try {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 2ae5c7b..ddda027 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -74,10 +74,6 @@
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
- final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- winAnimator.computeShownFrameLocked();
- // No need to lay out the windows - we can just set the wallpaper position directly.
- winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
// We only want to be synchronous with one wallpaper.
sync = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d96b27a..d12eee3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -100,15 +100,15 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.WindowManagerServiceProto.APP_TRANSITION;
-import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAY_FROZEN;
-import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_APP;
-import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION;
-import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY;
-import static com.android.server.wm.proto.WindowManagerServiceProto.ROOT_WINDOW_CONTAINER;
-import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.APP_TRANSITION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_APP;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.LAST_ORIENTATION;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.POLICY;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
+import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROTATION;
import android.Manifest;
import android.Manifest.permission;
@@ -1384,14 +1384,8 @@
win.attach();
mWindowMap.put(client.asBinder(), win);
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
- win.getOwningPackage());
- if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
- (startOpResult != AppOpsManager.MODE_DEFAULT)) {
- win.setAppOpVisibilityLw(false);
- }
- }
+
+ win.initAppOpsState();
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
@@ -1656,9 +1650,8 @@
void postWindowRemoveCleanupLocked(WindowState win) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
- }
+
+ win.resetAppOpsState();
if (mCurrentFocus == null) {
mWinRemovedSinceNullFocus.add(win);
@@ -2701,7 +2694,6 @@
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) {
synchronized (mWindowMap) {
- cancelRecentsAnimation();
mRecentsAnimationController = new RecentsAnimationController(this,
recentsAnimationRunner, callbacks, displayId);
mRecentsAnimationController.initialize();
@@ -2726,12 +2718,12 @@
}
public void cancelRecentsAnimation() {
- synchronized (mWindowMap) {
- if (mRecentsAnimationController != null) {
- // This call will call through to cleanupAnimation() below after the animation is
- // canceled
- mRecentsAnimationController.cancelAnimation();
- }
+ // Note: Do not hold the WM lock, this will lock appropriately in the call which also
+ // calls through to AM/RecentsAnimation.onAnimationFinished()
+ if (mRecentsAnimationController != null) {
+ // This call will call through to cleanupAnimation() below after the animation is
+ // canceled
+ mRecentsAnimationController.cancelAnimation();
}
}
@@ -2782,6 +2774,13 @@
mDockedStackCreateBounds = bounds;
}
+ public void checkSplitScreenMinimizedChanged(boolean animate) {
+ synchronized (mWindowMap) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.getDockedDividerController().checkMinimizeChanged(animate);
+ }
+ }
+
public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
@@ -4599,7 +4598,6 @@
public static final int NOTIFY_KEYGUARD_FLAGS_CHANGED = 56;
public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57;
public static final int SET_HAS_OVERLAY_UI = 58;
- public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
/**
* Used to denote that an integer field in a message will not be used.
@@ -5014,10 +5012,6 @@
mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
}
break;
- case SET_RUNNING_REMOTE_ANIMATION: {
- mAmInternal.setRunningRemoteAnimation(msg.arg1, msg.arg2 == 1);
- }
- break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -6193,7 +6187,7 @@
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
- * {@link com.android.server.wm.proto.WindowManagerServiceProto}.
+ * {@link com.android.server.wm.proto.WindowManagerServiceDumpProto}.
*
* @param proto Stream to write the WindowContainer object to.
* @param trim If true, reduce the amount of data written.
@@ -7446,10 +7440,5 @@
SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
return mSurfaceBuilderFactory.make(s);
}
-
- void sendSetRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
- mH.obtainMessage(H.SET_RUNNING_REMOTE_ANIMATION, pid, runningRemoteAnimation ? 1 : 0)
- .sendToTarget();
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b706096..22028cb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_NONE;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -138,7 +141,6 @@
import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS;
import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
@@ -298,12 +300,6 @@
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
- * Actual position of the surface shown on-screen (may be modified by animation). These are
- * in the screen's coordinate space (WITH the compatibility scale applied).
- */
- final Point mShownPosition = new Point();
-
- /**
* Insets that determine the actually visible area. These are in the application's
* coordinate space (without compatibility scale applied).
*/
@@ -462,10 +458,6 @@
int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
- // Wallpaper windows: pixels offset based on above variables.
- int mXOffset;
- int mYOffset;
-
/**
* This is set after IWindowSession.relayout() has been called at
* least once for the window. It allows us to detect the situation
@@ -771,8 +763,6 @@
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
- mXOffset = 0;
- mYOffset = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
@@ -1138,11 +1128,6 @@
}
@Override
- public Point getShownPositionLw() {
- return mShownPosition;
- }
-
- @Override
public Rect getDisplayFrameLw() {
return mDisplayFrame;
}
@@ -2571,7 +2556,7 @@
}
}
- public void setAppOpVisibilityLw(boolean state) {
+ private void setAppOpVisibilityLw(boolean state) {
if (mAppOpVisibility != state) {
mAppOpVisibility = state;
if (state) {
@@ -2588,6 +2573,49 @@
}
}
+ void initAppOpsState() {
+ if (mAppOp == OP_NONE || !mAppOpVisibility) {
+ return;
+ }
+ // If the app op was MODE_DEFAULT we would have checked the permission
+ // and add the window only if the permission was granted. Therefore, if
+ // the mode is MODE_DEFAULT we want the op to succeed as the window is
+ // shown.
+ final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+ getOwningUid(), getOwningPackage(), true);
+ if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+ setAppOpVisibilityLw(false);
+ }
+ }
+
+ void resetAppOpsState() {
+ if (mAppOp != OP_NONE && mAppOpVisibility) {
+ mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ }
+ }
+
+ void updateAppOpsState() {
+ if (mAppOp == OP_NONE) {
+ return;
+ }
+ final int uid = getOwningUid();
+ final String packageName = getOwningPackage();
+ if (mAppOpVisibility) {
+ // There is a race between the check and the finish calls but this is fine
+ // as this would mean we will get another change callback and will reconcile.
+ int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+ if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+ mService.mAppOps.finishOp(mAppOp, uid, packageName);
+ setAppOpVisibilityLw(false);
+ }
+ } else {
+ final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName);
+ if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
+ setAppOpVisibilityLw(true);
+ }
+ }
+ }
+
public void hidePermanentlyLw() {
if (!mPermanentlyHidden) {
mPermanentlyHidden = true;
@@ -3136,7 +3164,6 @@
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
- mShownPosition.writeToProto(proto, SHOWN_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
proto.write(ANIMATING_EXIT, mAnimatingExit);
for (int i = 0; i < mChildren.size(); i++) {
@@ -3252,10 +3279,6 @@
pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
}
- if (mXOffset != 0 || mYOffset != 0) {
- pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
- pw.print(" y="); pw.println(mYOffset);
- }
if (dumpAll) {
pw.print(prefix); pw.print("mGivenContentInsets=");
mGivenContentInsets.printShortString(pw);
@@ -3274,7 +3297,6 @@
pw.println(getLastReportedConfiguration());
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
if (dumpAll) {
@@ -4195,9 +4217,8 @@
final int width = mFrame.width();
final int height = mFrame.height();
- // Compute the offset of the window in relation to the decor rect.
- final int left = mXOffset + mFrame.left;
- final int top = mYOffset + mFrame.top;
+ final int left = mFrame.left;
+ final int top = mFrame.top;
// Initialize the decor rect to the entire frame.
if (isDockedResizing()) {
@@ -4299,14 +4320,9 @@
// When we change the Surface size, in scenarios which may require changing
// the surface position in sync with the resize, we use a preserved surface
// so we can freeze it while waiting for the client to report draw on the newly
- // sized surface. Don't preserve surfaces if the insets change while animating the pinned
- // stack since it can lead to issues if a new surface is created while calculating the
- // scale for the animation using the source hint rect
- // (see WindowStateAnimator#setSurfaceBoundariesLocked()).
- if (isDragResizeChanged()
- || (surfaceInsetsChanging() && !inPinnedWindowingMode())) {
- mLastSurfaceInsets.set(mAttrs.surfaceInsets);
-
+ // sized surface. At the moment this logic is only in place for switching
+ // in and out of the big surface for split screen resize.
+ if (isDragResizeChanged()) {
setDragResizing();
// We can only change top level windows to the full-screen surface when
// resizing (as we only have one full-screen surface). So there is no need
@@ -4391,8 +4407,8 @@
float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
- int x = mSurfacePosition.x + mShownPosition.x;
- int y = mSurfacePosition.y + mShownPosition.y;
+ int x = mSurfacePosition.x;
+ int y = mSurfacePosition.y;
// If changed, also adjust transformFrameToSurfacePosition
final WindowContainer parent = getParent();
@@ -4554,9 +4570,16 @@
}
transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+
if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
+ if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
+ mLastSurfaceInsets.set(mAttrs.surfaceInsets);
+ t.deferTransactionUntil(mSurfaceControl,
+ mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+ mAttrs.frameNumber);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9ce0537..0e80819 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -209,6 +209,12 @@
float mExtraHScale = (float) 1.0;
float mExtraVScale = (float) 1.0;
+ // An offset in pixel of the surface contents from the window position. Used for Wallpaper
+ // to provide the effect of scrolling within a large surface. We just use these values as
+ // a cache.
+ int mXOffset = 0;
+ int mYOffset = 0;
+
private final Rect mTmpSize = new Rect();
private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction();
@@ -438,7 +444,7 @@
flags |= SurfaceControl.SECURE;
}
- mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -679,8 +685,8 @@
// WindowState.prepareSurfaces expands for surface insets (in order they don't get
// clipped by the WindowState surface), so we need to go into the other direction here.
- tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
+ tmpMatrix.postTranslate(mWin.mAttrs.surfaceInsets.left,
+ mWin.mAttrs.surfaceInsets.top);
// "convert" it into SurfaceFlinger's format
@@ -695,9 +701,6 @@
mDtDx = tmpFloats[Matrix.MSKEW_Y];
mDtDy = tmpFloats[Matrix.MSKEW_X];
mDsDy = tmpFloats[Matrix.MSCALE_Y];
- float x = tmpFloats[Matrix.MTRANS_X];
- float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set(Math.round(x), Math.round(y));
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -707,8 +710,7 @@
mShownAlpha = mAlpha;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
- || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
- && x == frame.left && y == frame.top))) {
+ || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) {
//Slog.i(TAG_WM, "Applying alpha transform");
if (screenAnimation) {
mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
@@ -738,10 +740,6 @@
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- // WindowState.prepareSurfaces expands for surface insets (in order they don't get
- // clipped by the WindowState surface), so we need to go into the other direction here.
- mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
mShownAlpha = mAlpha;
mHaveMatrix = false;
mDsDx = mWin.mGlobalScale;
@@ -792,12 +790,6 @@
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
+ " fullscreen=" + fullscreen);
- if (isFreeformResizing && !w.isChildWindow()) {
- // For freeform resizing non child windows, we are using the big surface positioned
- // at 0,0. Thus we must express the crop in that coordinate space.
- clipRect.offset(w.mShownPosition.x, w.mShownPosition.y);
- }
-
w.expandForSurfaceInsets(clipRect);
// The clip rect was generated assuming (0,0) as the window origin,
@@ -834,7 +826,7 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
mExtraHScale = (float) 1.0;
@@ -870,17 +862,19 @@
float surfaceWidth = mSurfaceController.getWidth();
float surfaceHeight = mSurfaceController.getHeight();
+ final Rect insets = attrs.surfaceInsets;
+
if (isForceScaled()) {
- int hInsets = attrs.surfaceInsets.left + attrs.surfaceInsets.right;
- int vInsets = attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+ int hInsets = insets.left + insets.right;
+ int vInsets = insets.top + insets.bottom;
float surfaceContentWidth = surfaceWidth - hInsets;
float surfaceContentHeight = surfaceHeight - vInsets;
if (!mForceScaleUntilResize) {
mSurfaceController.forceScaleableInTransaction(true);
}
- int posX = mTmpSize.left;
- int posY = mTmpSize.top;
+ int posX = 0;
+ int posY = 0;
task.mStack.getDimBounds(mTmpStackBounds);
boolean allowStretching = false;
@@ -927,9 +921,19 @@
posX -= (int) (tw * mExtraHScale * mTmpSourceBounds.left);
posY -= (int) (th * mExtraVScale * mTmpSourceBounds.top);
- // Always clip to the stack bounds since the surface can be larger with the current
- // scale
- clipRect = null;
+ // In pinned mode the clip rectangle applied to us by our stack has been
+ // expanded outwards to allow for shadows. However in case of source bounds set
+ // we need to crop to within the surface. The code above has scaled and positioned
+ // the surface to fit the unexpanded stack bounds, but now we need to reapply
+ // the cropping that the stack would have applied if it weren't expanded. This
+ // can be different in each direction based on the source bounds.
+ clipRect = mTmpClipRect;
+ clipRect.set((int)((insets.left + mTmpSourceBounds.left) * tw),
+ (int)((insets.top + mTmpSourceBounds.top) * th),
+ insets.left + (int)(surfaceWidth
+ - (tw* (surfaceWidth - mTmpSourceBounds.right))),
+ insets.top + (int)(surfaceHeight
+ - (th * (surfaceHeight - mTmpSourceBounds.bottom))));
} else {
// We want to calculate the scaling based on the content area, not based on
// the entire surface, so that we scale in sync with windows that don't have insets.
@@ -955,8 +959,8 @@
// non inset content at the same position, we have to shift the whole window
// forward. Likewise for scaling up, we've increased this distance, and we need
// to shift by a negative number to compensate.
- posX += attrs.surfaceInsets.left * (1 - mExtraHScale);
- posY += attrs.surfaceInsets.top * (1 - mExtraVScale);
+ posX += insets.left * (1 - mExtraHScale);
+ posY += insets.top * (1 - mExtraVScale);
mSurfaceController.setPositionInTransaction((float) Math.floor(posX),
(float) Math.floor(posY), recoveringMemory);
@@ -970,8 +974,7 @@
mForceScaleUntilResize = true;
} else {
if (!w.mSeamlesslyRotated) {
- mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
- recoveringMemory);
+ mSurfaceController.setPositionInTransaction(0, 0, recoveringMemory);
}
}
@@ -1139,24 +1142,26 @@
mSurfaceController.setTransparentRegionHint(region);
}
- void setWallpaperOffset(Point shownPosition) {
- final LayoutParams attrs = mWin.getAttrs();
- final int left = shownPosition.x - attrs.surfaceInsets.left;
- final int top = shownPosition.y - attrs.surfaceInsets.top;
+ boolean setWallpaperOffset(int dx, int dy) {
+ if (mXOffset == dx && mYOffset == dy) {
+ return false;
+ }
+ mXOffset = dx;
+ mYOffset = dy;
try {
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
mService.openSurfaceTransaction();
- mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
- mWin.mFrame.top + top, false);
+ mSurfaceController.setPositionInTransaction(dx, dy, false);
applyCrop(null, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
- + " pos=(" + left + "," + top + ")", e);
+ + " pos=(" + dx + "," + dy + ")", e);
} finally {
mService.closeSurfaceTransaction("setWallpaperOffset");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setWallpaperOffset");
+ return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 66c7293..286cc49 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
@@ -608,6 +609,10 @@
if (transit == TRANSIT_NONE) {
return TRANSIT_NONE;
}
+ // Never update the transition for the wallpaper if we are just docking from recents
+ if (transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return TRANSIT_DOCK_TASK_FROM_RECENTS;
+ }
// if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9fcc348..6a468b1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -160,6 +160,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -220,6 +221,7 @@
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
+import com.android.server.storage.DeviceStorageMonitorInternal;
import com.google.android.collect.Sets;
@@ -8884,13 +8886,40 @@
final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
&& UserManager.isDeviceInDemoMode(mContext);
final boolean leaveAllSystemAppsEnabled = (flags & LEAVE_ALL_SYSTEM_APPS_ENABLED) != 0;
+ final int targetSdkVersion;
+
// Create user.
UserHandle user = null;
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final int callingUid = mInjector.binderGetCallingUid();
final long id = mInjector.binderClearCallingIdentity();
try {
+ targetSdkVersion = mInjector.getPackageManagerInternal().getUidTargetSdkVersion(
+ callingUid);
+
+ // Return detail error code for checks inside
+ // UserManagerService.createUserInternalUnchecked.
+ DeviceStorageMonitorInternal deviceStorageMonitorInternal =
+ LocalServices.getService(DeviceStorageMonitorInternal.class);
+ if (deviceStorageMonitorInternal.isMemoryLow()) {
+ if (targetSdkVersion >= Build.VERSION_CODES.P) {
+ throw new ServiceSpecificException(
+ UserManager.USER_OPERATION_ERROR_LOW_STORAGE, "low device storage");
+ } else {
+ return null;
+ }
+ }
+ if (!mUserManager.canAddMoreUsers()) {
+ if (targetSdkVersion >= Build.VERSION_CODES.P) {
+ throw new ServiceSpecificException(
+ UserManager.USER_OPERATION_ERROR_MAX_USERS, "user limit reached");
+ } else {
+ return null;
+ }
+ }
+
int userInfoFlags = 0;
if (ephemeral) {
userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
@@ -8914,7 +8943,12 @@
}
}
if (user == null) {
- return null;
+ if (targetSdkVersion >= Build.VERSION_CODES.P) {
+ throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
+ "failed to create user");
+ } else {
+ return null;
+ }
}
final int userHandle = user.getIdentifier();
@@ -8960,7 +8994,12 @@
return user;
} catch (Throwable re) {
mUserManager.removeUser(userHandle);
- return null;
+ if (targetSdkVersion >= Build.VERSION_CODES.P) {
+ throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
+ re.getMessage());
+ } else {
+ return null;
+ }
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -9041,24 +9080,24 @@
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
Log.w(LOG_TAG, "Managed profile cannot be started in background");
- return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+ return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
final long id = mInjector.binderClearCallingIdentity();
try {
if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
Log.w(LOG_TAG, "Cannot start more users in background");
- return DevicePolicyManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
+ return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
}
if (mInjector.getIActivityManager().startUserInBackground(userId)) {
- return DevicePolicyManager.USER_OPERATION_SUCCESS;
+ return UserManager.USER_OPERATION_SUCCESS;
} else {
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
} catch (RemoteException e) {
// Same process, should not happen.
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -9076,7 +9115,7 @@
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
Log.w(LOG_TAG, "Managed profile cannot be stopped");
- return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+ return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
return stopUserUnchecked(userId);
@@ -9097,7 +9136,7 @@
if (isManagedProfile(callingUserId)) {
Log.w(LOG_TAG, "Managed profile cannot be logout");
- return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+ return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
final long id = mInjector.binderClearCallingIdentity();
@@ -9105,11 +9144,11 @@
if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
Log.w(LOG_TAG, "Failed to switch to primary user");
// This should never happen as target user is UserHandle.USER_SYSTEM
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
} catch (RemoteException e) {
// Same process, should not happen.
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -9122,15 +9161,15 @@
try {
switch (mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)) {
case ActivityManager.USER_OP_SUCCESS:
- return DevicePolicyManager.USER_OPERATION_SUCCESS;
+ return UserManager.USER_OPERATION_SUCCESS;
case ActivityManager.USER_OP_IS_CURRENT:
- return DevicePolicyManager.USER_OPERATION_ERROR_CURRENT_USER;
+ return UserManager.USER_OPERATION_ERROR_CURRENT_USER;
default:
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
} catch (RemoteException e) {
// Same process, should not happen.
- return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index 8372778..f603a09 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -19,6 +19,7 @@
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThreadAndGetLooper;
+
import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.google.common.truth.Truth.assertThat;
@@ -103,18 +104,10 @@
@Config(
manifest = Config.NONE,
sdk = 26,
- shadows = {
- ShadowBackupDataInput.class,
- ShadowBackupDataOutput.class,
- ShadowQueuedWork.class
- }
+ shadows = {ShadowBackupDataInput.class, ShadowBackupDataOutput.class, ShadowQueuedWork.class}
)
@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"})
-@SystemLoaderClasses({
- IBackupTransport.class,
- IBackupAgent.class,
- PackageInfo.class
-})
+@SystemLoaderClasses({IBackupTransport.class, IBackupAgent.class, PackageInfo.class})
@Presubmit
public class PerformBackupTaskTest {
private static final String PACKAGE_1 = "com.example.package1";
@@ -581,6 +574,7 @@
return task;
}
+ /** Matches {@link PackageInfo} whose package name is {@code packageName}. */
private static ArgumentMatcher<PackageInfo> packageInfo(String packageName) {
// We have to test for packageInfo nulity because of Mockito's own stubbing with argThat().
// E.g. if you do:
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 6abd30c..7d1b6c86 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -92,7 +92,6 @@
private TransportData mTransportA1;
private TransportData mTransportA2;
private TransportData mTransportB1;
-
private ShadowPackageManager mShadowPackageManager;
private Context mContext;
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index c6a4f57..03792b1 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -39,6 +39,8 @@
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.RestoreSet;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -65,7 +67,9 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowBinder;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayDeque;
@@ -73,13 +77,15 @@
@Config(
manifest = Config.NONE,
sdk = 26,
- shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class}
+ shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class}
)
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class ActiveRestoreSessionTest {
private static final String PACKAGE_1 = "com.example.package1";
private static final String PACKAGE_2 = "com.example.package2";
+ public static final long TOKEN_1 = 1L;
+ public static final long TOKEN_2 = 2L;
@Mock private BackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@@ -89,10 +95,9 @@
private ShadowApplication mShadowApplication;
private PowerManager.WakeLock mWakeLock;
private TransportData mTransport;
- private long mToken1;
- private long mToken2;
private RestoreSet mRestoreSet1;
private RestoreSet mRestoreSet2;
+ private ShadowPackageManager mShadowPackageManager;
@Before
public void setUp() throws Exception {
@@ -100,14 +105,14 @@
mTransport = backupTransport();
- mToken1 = 1L;
- mRestoreSet1 = new RestoreSet("name1", "device1", mToken1);
- mToken2 = 2L;
- mRestoreSet2 = new RestoreSet("name2", "device2", mToken2);
+ mRestoreSet1 = new RestoreSet("name1", "device1", TOKEN_1);
+ mRestoreSet2 = new RestoreSet("name2", "device2", TOKEN_2);
Application application = RuntimeEnvironment.application;
mShadowApplication = shadowOf(application);
+ mShadowPackageManager = shadowOf(application.getPackageManager());
+
Looper backupLooper = startBackupThreadAndGetLooper();
mShadowBackupLooper = shadowOf(backupLooper);
BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
@@ -223,7 +228,7 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
- int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+ int result = restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor);
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
@@ -245,11 +250,9 @@
setUpTransport(mTransport);
IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
- int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+ int result = restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor);
- mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
}
@Test
@@ -259,11 +262,9 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(PACKAGE_1, mTransport, mRestoreSet1);
- int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+ int result = restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor);
- mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
}
@Test
@@ -277,7 +278,7 @@
expectThrows(
IllegalStateException.class,
- () -> restoreSession.restoreAll(mToken1, mObserver, mMonitor));
+ () -> restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor));
}
@Test
@@ -287,11 +288,9 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
- int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+ int result = restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor);
- mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
}
@Test
@@ -302,7 +301,7 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
- int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+ int result = restoreSession.restoreAll(TOKEN_1, mObserver, mMonitor);
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
@@ -318,7 +317,7 @@
int result =
restoreSession.restoreSome(
- mToken1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
+ TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
@@ -340,7 +339,7 @@
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
restoreSession.restoreSome(
- mToken1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
+ TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
mShadowBackupLooper.runToEndOfTasks();
assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore()).isTrue();
@@ -353,7 +352,7 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
- restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+ restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1});
mShadowBackupLooper.runToEndOfTasks();
ShadowPerformUnifiedRestoreTask shadowTask =
@@ -369,7 +368,7 @@
IRestoreSession restoreSession =
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
- restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+ restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1});
mShadowBackupLooper.runToEndOfTasks();
assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore())
@@ -383,11 +382,9 @@
IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
int result =
- restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+ restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1});
- mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
}
@Test
@@ -398,11 +395,9 @@
createActiveRestoreSessionWithRestoreSets(PACKAGE_1, mTransport, mRestoreSet1);
int result =
- restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_2});
+ restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_2});
- mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
}
@Test
@@ -418,7 +413,7 @@
IllegalStateException.class,
() ->
restoreSession.restoreSome(
- mToken1, mObserver, mMonitor, new String[] {PACKAGE_1}));
+ TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1}));
}
@Test
@@ -429,11 +424,125 @@
createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
int result =
- restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+ restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1});
+
+ assertThat(result).isEqualTo(-1);
+ }
+
+ @Test
+ public void testRestorePackage_whenCallerIsPackage() throws Exception {
+ // No need for BACKUP permission in this case
+ mShadowApplication.denyPermissions(android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 1);
+ when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
+ TransportMock transportMock = setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(PACKAGE_1, mTransport);
+
+ int result = restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor);
mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(0);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ assertThat(mWakeLock.isHeld()).isFalse();
+ assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
+ ShadowPerformUnifiedRestoreTask shadowTask =
+ ShadowPerformUnifiedRestoreTask.getLastCreated();
+ assertThat(shadowTask.isFullSystemRestore()).isFalse();
+ assertThat(shadowTask.getFilterSet()).isNull();
+ assertThat(shadowTask.getPackage().packageName).isEqualTo(PACKAGE_1);
+ }
+
+ @Test
+ public void testRestorePackage_whenPackageNullWhenCreated() throws Exception {
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 1);
+ when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
+ setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+
+ int result = restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(0);
+ }
+
+ @Test
+ public void testRestorePackage_whenCallerIsNotPackageAndPermissionGranted() throws Exception {
+ mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 2);
+ when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
+ setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(PACKAGE_1, mTransport);
+
+ int result = restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(0);
+ }
+
+ @Test
+ public void testRestorePackage_whenCallerIsNotPackageAndPermissionDenied() throws Exception {
+ mShadowApplication.denyPermissions(android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 2);
+ when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
+ setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(PACKAGE_1, mTransport);
+
+ expectThrows(
+ SecurityException.class,
+ () -> restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor));
+ }
+
+ @Test
+ public void testRestorePackage_whenPackageNotFound() throws Exception {
+ mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpPackage(PACKAGE_1, /* uid */ 1);
+ setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+
+ int result = restoreSession.restorePackage(PACKAGE_2, mObserver, mMonitor);
+
assertThat(result).isEqualTo(-1);
- assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+ }
+
+ @Test
+ public void testRestorePackage_whenSessionEnded() throws Exception {
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 1);
+ setUpTransport(mTransport);
+ IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+ restoreSession.endRestoreSession();
+ mShadowBackupLooper.runToEndOfTasks();
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor));
+ }
+
+ @Test
+ public void testRestorePackage_whenTransportNotRegistered() throws Exception {
+ ShadowBinder.setCallingUid(1);
+ setUpPackage(PACKAGE_1, /* uid */ 1);
+ setUpTransport(mTransport.unregistered());
+ IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+
+ int result = restoreSession.restorePackage(PACKAGE_1, mObserver, mMonitor);
+
+ assertThat(result).isEqualTo(-1);
+ }
+
+ // TODO: Create a builder for PackageInfo/ApplicationInfo and unify usage with
+ // TransportManagerTest
+ private void setUpPackage(String packageName, int uid) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.uid = uid;
+ mShadowPackageManager.addPackage(packageInfo);
}
private IRestoreSession createActiveRestoreSession(
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 3c0234b..c210fde 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -34,8 +34,9 @@
import java.lang.Thread.UncaughtExceptionHandler;
+/** Test utils for {@link BackupManagerService} and friends. */
public class BackupManagerServiceTestUtils {
- /** Sets up a basic mocks for {@link BackupManagerService} */
+ /** Sets up basic mocks for {@link BackupManagerService}. */
public static void setUpBackupManagerServiceBasics(
BackupManagerService backupManagerService,
Context context,
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index b3fac48..356e64b 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -42,6 +42,7 @@
android.test.base android.test.runner \
LOCAL_PACKAGE_NAME := FrameworksServicesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index fb1595e..a527e17 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -111,8 +111,6 @@
assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
}
- // TODO: b/71582913
- @Ignore("b/71582913")
@Test
public void testPausingWhenVisibleFromStopped() throws Exception {
final MutableBoolean pauseFound = new MutableBoolean(false);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 86c83d6..8ccacb8 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -18,9 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
@@ -30,6 +35,7 @@
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.testutils.PackageManagerStub;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +51,14 @@
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
- private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
+ private PackageManagerStub mPackageManagerStub;
+ private PackageManagerInternal mMockPackageManagerInternal;
+
+ @Before
+ public void setUp() throws Exception {
+ mPackageManagerStub = new PackageManagerStub();
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ }
@Test
public void appIsEligibleForBackup_backupNotAllowed_returnsFalse() throws Exception {
@@ -358,7 +371,8 @@
@Test
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -369,7 +383,8 @@
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isTrue();
}
@@ -378,10 +393,11 @@
public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[] {SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -390,10 +406,11 @@
public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[] {SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -404,11 +421,11 @@
signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[0];
+ packageInfo.signingCertificateHistory = new Signature[0][0];
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
- packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -418,11 +435,11 @@
signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = null;
+ packageInfo.signingCertificateHistory = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
- packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -431,10 +448,11 @@
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = null;
+ packageInfo.signingCertificateHistory = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -443,10 +461,11 @@
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[0];
+ packageInfo.signingCertificateHistory = new Signature[0][0];
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -458,11 +477,14 @@
Signature signature3Copy = new Signature(SIGNATURE_3.toByteArray());
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{signature3Copy, signature1Copy, signature2Copy}, packageInfo);
+ new Signature[] {signature3Copy, signature1Copy, signature2Copy}, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isTrue();
}
@@ -473,11 +495,14 @@
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{signature2Copy, signature1Copy}, packageInfo);
+ new Signature[]{signature2Copy, signature1Copy}, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isTrue();
}
@@ -488,11 +513,14 @@
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[]{signature1Copy, signature2Copy};
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {signature1Copy, signature2Copy}
+ };
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo);
+ new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo,
+ mMockPackageManagerInternal);
assertThat(result).isFalse();
}
@@ -503,11 +531,76 @@
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo);
+ new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
+ throws Exception {
+ Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
+ packageInfo.packageName);
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo, mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
+ throws Exception {
+ Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ // we know signature1Copy is in history, and we want to assume it has
+ // SigningDetails.CertCapabilities.INSTALLED_DATA capability
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
+ packageInfo.packageName);
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo, mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void
+ signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
+ throws Exception {
+ Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ // we know signature1Copy is in history, but we want to assume it does not have
+ // SigningDetails.CertCapabilities.INSTALLED_DATA capability
+ doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
+ packageInfo.packageName);
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo, mMockPackageManagerInternal);
assertThat(result).isFalse();
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 0cdf04b..5f052ce 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -28,6 +28,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -37,6 +40,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.Process;
@@ -79,6 +83,7 @@
@Mock private BytesReadListener mBytesReadListenerMock;
@Mock private IBackupManagerMonitor mBackupManagerMonitorMock;
+ @Mock private PackageManagerInternal mMockPackageManagerInternal;
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
private Context mContext;
@@ -139,7 +144,8 @@
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
fileMetadata);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
- mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
+ mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
+ mMockPackageManagerInternal);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -152,7 +158,8 @@
signatures = tarBackupReader.readAppManifestAndReturnSignatures(
fileMetadata);
restorePolicy = tarBackupReader.chooseRestorePolicy(
- mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
+ mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
+ mMockPackageManagerInternal);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -214,7 +221,8 @@
mBytesReadListenerMock, mBackupManagerMonitorMock);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, new FileMetadata(), null /* signatures */);
+ true /* allowApks */, new FileMetadata(), null /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
verifyZeroInteractions(mBackupManagerMonitorMock);
@@ -234,7 +242,8 @@
PackageManagerStub.sPackageInfo = null;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, info, new Signature[0] /* signatures */);
+ true /* allowApks */, info, new Signature[0] /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -258,7 +267,8 @@
PackageManagerStub.sPackageInfo = null;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, info, new Signature[0] /* signatures */);
+ true /* allowApks */, info, new Signature[0] /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -283,7 +293,8 @@
PackageManagerStub.sPackageInfo = null;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
+ false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -307,7 +318,8 @@
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
+ false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -333,7 +345,8 @@
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
+ false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
+ mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -358,11 +371,11 @@
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_2};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_2}};
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures);
+ false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -383,16 +396,19 @@
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |=
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
packageInfo.applicationInfo.backupAgentName = "backup.agent";
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
PackageManagerStub.sPackageInfo = packageInfo;
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures);
+ false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -412,16 +428,19 @@
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |=
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
PackageManagerStub.sPackageInfo = packageInfo;
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures);
+ false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -444,17 +463,20 @@
info.version = 1;
PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
packageInfo.versionCode = 2;
PackageManagerStub.sPackageInfo = packageInfo;
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -479,17 +501,20 @@
info.hasApk = true;
PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, info, signatures);
+ true /* allowApks */, info, signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
verifyNoMoreInteractions(mBackupManagerMonitorMock);
@@ -510,17 +535,20 @@
info.version = 2;
PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
+ packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 501f966..b4f8474 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -338,6 +338,26 @@
assertFalse(event.isDefaultBrightnessConfig);
assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
assertFalse(event.isUserSetBrightness);
+
+ // Pretend user 1 is a profile of user 0.
+ mInjector.mProfiles = new int[]{0, 1};
+ events = tracker.getEvents(0, true).getList();
+ // Both events should now be returned.
+ assertEquals(2, events.size());
+ BrightnessChangeEvent userZeroEvent;
+ BrightnessChangeEvent userOneEvent;
+ if (events.get(0).userId == 0) {
+ userZeroEvent = events.get(0);
+ userOneEvent = events.get(1);
+ } else {
+ userZeroEvent = events.get(1);
+ userOneEvent = events.get(0);
+ }
+ assertEquals(0, userZeroEvent.userId);
+ assertEquals("com.example.app", userZeroEvent.packageName);
+ assertEquals(1, userOneEvent.userId);
+ // Events from user 1 should have the package name redacted
+ assertNull(userOneEvent.packageName);
}
@Test
@@ -597,6 +617,7 @@
Handler mHandler;
boolean mIdleScheduled;
boolean mInteractive = true;
+ int[] mProfiles;
public TestInjector(Handler handler) {
mHandler = handler;
@@ -682,6 +703,15 @@
}
@Override
+ public int[] getProfileIds(UserManager userManager, int userId) {
+ if (mProfiles != null) {
+ return mProfiles;
+ } else {
+ return new int[]{userId};
+ }
+ }
+
+ @Override
public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
ActivityManager.StackInfo focusedStack = new ActivityManager.StackInfo();
focusedStack.userId = 0;
@@ -689,15 +719,18 @@
return focusedStack;
}
+ @Override
public void scheduleIdleJob(Context context) {
// Don't actually schedule jobs during unit tests.
mIdleScheduled = true;
}
+ @Override
public void cancelIdleJob(Context context) {
mIdleScheduled = false;
}
+ @Override
public boolean isInteractive(Context context) {
return mInteractive;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 199410c..e6a36c6 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
@@ -277,7 +277,7 @@
}
@Test
- public void initRecoveryService_succeeds() throws Exception {
+ public void initRecoveryService_succeedsWithCertFile() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
long certSerial = 1000L;
@@ -566,7 +566,31 @@
TEST_SECRET)));
fail("should have thrown");
} catch (ServiceSpecificException e) {
- assertThat(e.getMessage()).contains("CertPath is empty");
+ assertThat(e.getMessage()).contains("empty");
+ }
+ }
+
+ @Test
+ public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ CertPath shortCertPath = certFactory.generateCertPath(
+ TestData.CERT_PATH_1.getCertificates()
+ .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1));
+ try {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(shortCertPath),
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(
+ new KeyChainProtectionParams(
+ TYPE_LOCKSCREEN,
+ UI_FORMAT_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ // expected
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
index d08dab4..9279698 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
@@ -30,8 +30,10 @@
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
@@ -317,6 +319,42 @@
}
@Test
+ public void validateCertPath_succeeds() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_2;
+ CertPath certPath =
+ CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert));
+ CertUtils.validateCertPath(
+ TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED, certPath);
+ }
+
+ @Test
+ public void validateCertPath_throwsIfEmptyCertPath() throws Exception {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>());
+ CertValidationException expected =
+ expectThrows(
+ CertValidationException.class,
+ () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID,
+ TestData.ROOT_CA_TRUSTED, emptyCertPath));
+ assertThat(expected.getMessage()).contains("empty");
+ }
+
+ @Test
+ public void validateCertPath_throwsIfNotValidated() throws Exception {
+ assertThrows(
+ CertValidationException.class,
+ () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID,
+ TestData.ROOT_CA_DIFFERENT_COMMON_NAME,
+ com.android.server.locksettings.recoverablekeystore.TestData.CERT_PATH_1));
+ }
+
+ @Test
public void validateCert_succeeds() throws Exception {
X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
List<X509Certificate> intermediateCerts =
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index c016e61..a0cefbf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -15,73 +15,298 @@
*/
package com.android.server.pm.backup;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.Package;
import android.content.pm.Signature;
-import android.test.AndroidTestCase;
import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import com.android.server.backup.BackupUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.ArrayList;
import java.util.Arrays;
@SmallTest
-public class BackupUtilsTest extends AndroidTestCase {
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupUtilsTest {
- private Signature[] genSignatures(String... signatures) {
- final Signature[] sigs = new Signature[signatures.length];
- for (int i = 0; i < signatures.length; i++){
- sigs[i] = new Signature(signatures[i].getBytes());
- }
- return sigs;
+ private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
+ private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
+ private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
+ private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
+ private static final byte[] SIGNATURE_HASH_1 = BackupUtils.hashSignature(SIGNATURE_1);
+ private static final byte[] SIGNATURE_HASH_2 = BackupUtils.hashSignature(SIGNATURE_2);
+ private static final byte[] SIGNATURE_HASH_3 = BackupUtils.hashSignature(SIGNATURE_3);
+ private static final byte[] SIGNATURE_HASH_4 = BackupUtils.hashSignature(SIGNATURE_4);
+
+ private PackageManagerInternal mMockPackageManagerInternal;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
}
- private PackageInfo genPackage(String... signatures) {
- final PackageInfo pi = new PackageInfo();
- pi.packageName = "package";
- pi.applicationInfo = new ApplicationInfo();
- pi.signatures = genSignatures(signatures);
+ @Test
+ public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, null,
+ mMockPackageManagerInternal);
- return pi;
+ assertThat(result).isFalse();
}
- public void testSignaturesMatch() {
- final ArrayList<byte[]> stored1 = BackupUtils.hashSignatureArray(Arrays.asList(
- "abc".getBytes()));
- final ArrayList<byte[]> stored2 = BackupUtils.hashSignatureArray(Arrays.asList(
- "abc".getBytes(), "def".getBytes()));
+ @Test
+ public void signaturesMatch_systemApplication_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- PackageInfo pi;
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
- // False for null package.
- assertFalse(BackupUtils.signaturesMatch(stored1, null));
-
- // If it's a system app, signatures don't matter.
- pi = genPackage("xyz");
- pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- assertTrue(BackupUtils.signaturesMatch(stored1, pi));
-
- // Non system apps.
- assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc")));
-
- // Superset is okay.
- assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc", "xyz")));
- assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "abc")));
-
- assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz")));
- assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "def")));
-
- assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("def", "abc")));
- assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("x", "def", "abc", "y")));
-
- // Subset is not okay.
- assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("abc")));
- assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("def")));
+ assertThat(result).isTrue();
}
+ @Test
+ public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = BackupUtils.signaturesMatch(null, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+
+ @Test
+ public void
+ signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void
+ signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = null;
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = null;
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = BackupUtils.signaturesMatch(null, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ storedSigHashes.add(SIGNATURE_HASH_2);
+ storedSigHashes.add(SIGNATURE_HASH_3);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ storedSigHashes.add(SIGNATURE_HASH_2);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1, SIGNATURE_2}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ storedSigHashes.add(SIGNATURE_HASH_2);
+ storedSigHashes.add(SIGNATURE_HASH_3);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signingCertificateHistory = new Signature[][] {
+ {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
+ };
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ storedSigHashes.add(SIGNATURE_HASH_2);
+ storedSigHashes.add(SIGNATURE_HASH_4);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
+ packageInfo.packageName);
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ // we know SIGNATURE_1 is in history, and we want to assume it has
+ // SigningDetails.CertCapabilities.INSTALLED_DATA capability
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
+ packageInfo.packageName);
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void
+ signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ // we know SIGNATURE_1 is in history, but we want to assume it does not have
+ // SigningDetails.CertCapabilities.INSTALLED_DATA capability
+ doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
+ packageInfo.packageName);
+
+ ArrayList<byte[]> storedSigHashes = new ArrayList<>();
+ storedSigHashes.add(SIGNATURE_HASH_1);
+ boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
+ mMockPackageManagerInternal);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
public void testHashSignature() {
final byte[] sig1 = "abc".getBytes();
final byte[] sig2 = "def".getBytes();
@@ -115,4 +340,10 @@
MoreAsserts.assertEquals(hash2a, listA.get(1));
MoreAsserts.assertEquals(hash2a, listB.get(1));
}
+
+ private static Signature generateSignature(byte i) {
+ byte[] signatureBytes = new byte[256];
+ signatureBytes[0] = i;
+ return new Signature(signatureBytes);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a628b7b..5de393c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -79,11 +79,6 @@
}
@Override
- public Point getShownPositionLw() {
- return new Point(parentFrame.left, parentFrame.top);
- }
-
- @Override
public Rect getDisplayFrameLw() {
return displayFrame;
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 30ca9ca..1d4348c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -77,7 +77,9 @@
public void setUpBase() throws Exception {
mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext());
mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.status_bar_height, STATUS_BAR_HEIGHT);
+ com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
mContext.getResourceMocker().addOverride(
com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
mContext.getResourceMocker().addOverride(
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
index fbfa28a..18b8c2d 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
@@ -24,6 +24,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := ConnTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index d7c3f7f..b98bc89 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -31,6 +31,7 @@
LOCAL_DX_FLAGS := --multi-dex
LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 3fbcd81..7f060b3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -47,7 +47,7 @@
class UserUsageStatsService {
private static final String TAG = "UsageStatsService";
private static final boolean DEBUG = UsageStatsService.DEBUG;
- private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd/HH:mm:ss");
private static final int sDateFormatFlags =
DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_SHOW_TIME
@@ -572,11 +572,13 @@
pw.printPair("endTime", endTime);
}
pw.println(")");
- pw.increaseIndent();
- for (UsageEvents.Event event : events) {
- printEvent(pw, event, prettyDates);
+ if (events != null) {
+ pw.increaseIndent();
+ for (UsageEvents.Event event : events) {
+ printEvent(pw, event, prettyDates);
+ }
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
}
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ddb4f04..33da403 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -138,6 +138,7 @@
}
public void systemReady() {
+ mSystemReady = true;
if (mProxy != null) {
try {
mProxy.queryPortStatus();
@@ -146,7 +147,6 @@
"ServiceStart: Failed to query port status", e);
}
}
- mSystemReady = true;
}
public UsbPort[] getPorts() {
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index e7f0cc2..cb87d1f 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -1103,10 +1103,15 @@
"android.provider.Telephony.MMS_DOWNLOADED";
/**
- * Broadcast Action: A debug code has been entered in the dialer. These "secret codes"
- * are used to activate developer menus by dialing certain codes. And they are of the
- * form {@code *#*#<code>#*#*}. The intent will have the data URI:
- * {@code android_secret_code://<code>}.
+ * Broadcast Action: A debug code has been entered in the dialer. This intent is
+ * broadcast by the system and OEM telephony apps may need to receive these broadcasts.
+ * These "secret codes" are used to activate developer menus by dialing certain codes.
+ * And they are of the form {@code *#*#<code>#*#*}. The intent will have the data
+ * URI: {@code android_secret_code://<code>}. It is possible that a manifest
+ * receiver would be woken up even if it is not currently running.
+ *
+ * <p>Requires {@code android.Manifest.permission#CONTROL_INCALL_EXPERIENCE} to
+ * send and receive.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SECRET_CODE_ACTION =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cdc1ba9..af3a0bb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5281,6 +5281,24 @@
}
/**
+ * Determines if emergency calling is allowed for the MMTEL feature on the slot provided.
+ * @param slotIndex The SIM slot of the MMTEL feature
+ * @return true if emergency calling is allowed, false otherwise.
+ * @hide
+ */
+ public boolean isEmergencyMmTelAvailable(int slotIndex) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isEmergencyMmTelAvailable(slotIndex);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "isEmergencyMmTelAvailable, RemoteException: " + e.getMessage());
+ }
+ return false;
+ }
+
+ /**
* Set IMS registration state
*
* @param Registration state
@@ -7183,18 +7201,16 @@
*
* @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
- * @throws IllegalStateException if telephony service is unavailable.
*/
public int getAndroidCarrierIdForSubscription() {
try {
ITelephony service = getITelephony();
- return service.getSubscriptionCarrierId(getSubId());
+ if (service != null) {
+ return service.getSubscriptionCarrierId(getSubId());
+ }
} catch (RemoteException ex) {
// This could happen if binder process crashes.
ex.rethrowAsRuntimeException();
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing.
- throw new IllegalStateException("Telephony service unavailable");
}
return UNKNOWN_CARRIER_ID;
}
@@ -7210,18 +7226,16 @@
*
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
- * @throws IllegalStateException if telephony service is unavailable.
*/
public CharSequence getAndroidCarrierNameForSubscription() {
try {
ITelephony service = getITelephony();
- return service.getSubscriptionCarrierName(getSubId());
+ if (service != null) {
+ return service.getSubscriptionCarrierName(getSubId());
+ }
} catch (RemoteException ex) {
// This could happen if binder process crashes.
ex.rethrowAsRuntimeException();
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing.
- throw new IllegalStateException("Telephony service unavailable");
}
return null;
}
@@ -7669,4 +7683,83 @@
Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
}
}
+
+ /**
+ * In this mode, modem will not send specified indications when screen is off.
+ * @hide
+ */
+ public static final int INDICATION_UPDATE_MODE_NORMAL = 1;
+
+ /**
+ * In this mode, modem will still send specified indications when screen is off.
+ * @hide
+ */
+ public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = {
+ INDICATION_UPDATE_MODE_NORMAL,
+ INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IndicationUpdateMode{}
+
+ /**
+ * The indication for signal strength update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_SIGNAL_STRENGTH = 0x1;
+
+ /**
+ * The indication for full network state update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_FULL_NETWORK_STATE = 0x2;
+
+ /**
+ * The indication for data call dormancy changed update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED = 0x4;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
+ INDICATION_FILTER_SIGNAL_STRENGTH,
+ INDICATION_FILTER_FULL_NETWORK_STATE,
+ INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IndicationFilters{}
+
+ /**
+ * Sets radio indication update mode. This can be used to control the behavior of indication
+ * update from modem to Android frameworks. For example, by default several indication updates
+ * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
+ * screen is off) we want to turn on those indications even when the screen is off.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
+ * @see #INDICATION_FILTER_SIGNAL_STRENGTH
+ * @see #INDICATION_FILTER_FULL_NETWORK_STATE
+ * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+ * @param updateMode The voice activation state
+ * @see #INDICATION_UPDATE_MODE_NORMAL
+ * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRadioIndicationUpdateMode(@IndicationFilters int filters,
+ @IndicationUpdateMode int updateMode) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 1fdbae9..d537699 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -87,7 +87,9 @@
// ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
// defined values in ImsServiceClass for compatibility purposes.
/**
- * This feature supports emergency calling over MMTEL.
+ * This feature supports emergency calling over MMTEL. If defined, the framework will try to
+ * place an emergency call over IMS first. If it is not defined, the framework will only use
+ * CSFB for emergency calling.
*/
public static final int FEATURE_EMERGENCY_MMTEL = 0;
/**
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 09267fc..2fffd36 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -332,20 +332,14 @@
public static final int PROCESS_CALL_IMS = 0;
/**
* To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
- * not process the outgoing NON_EMERGENCY call as IMS and should instead use circuit switch.
+ * not process the outgoing call as IMS and should instead use circuit switch.
*/
public static final int PROCESS_CALL_CSFB = 1;
- /**
- * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
- * not process the outgoing EMERGENCY call as IMS and should instead use circuit switch.
- */
- public static final int PROCESS_CALL_EMERGENCY_CSFB = 2;
@IntDef(flag = true,
value = {
PROCESS_CALL_IMS,
- PROCESS_CALL_CSFB,
- PROCESS_CALL_EMERGENCY_CSFB
+ PROCESS_CALL_CSFB
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
@@ -536,12 +530,15 @@
/**
* Called by the framework to determine if the outgoing call, designated by the outgoing
- * {@link Uri}s, should be processed as an IMS call or CSFB call.
+ * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
+ * functionality is not overridden, the platform will process every call as IMS as long as the
+ * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
+ * available.
* @param numbers An array of {@link String}s that will be used for placing the call. There can
* be multiple {@link String}s listed in the case when we want to place an outgoing
* call as a conference.
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
- * call wil lbe placed over IMS or via CSFB.
+ * call will be placed over IMS or via CSFB.
*/
public @ProcessCallResult int shouldProcessCall(String[] numbers) {
return PROCESS_CALL_IMS;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9e2b519..a941a56 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -823,6 +823,12 @@
IImsConfig getImsConfig(int slotId, int feature);
/**
+ * Returns true if emergency calling is available for the MMTEL feature associated with the
+ * slot specified.
+ */
+ boolean isEmergencyMmTelAvailable(int slotId);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
@@ -1473,4 +1479,12 @@
* @return boolean Return true if the switch succeeds, false if the switch fails.
*/
boolean switchSlots(in int[] physicalSlots);
+
+ /**
+ * Sets radio indication update mode. This can be used to control the behavior of indication
+ * update from modem to Android frameworks. For example, by default several indication updates
+ * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
+ * screen is off) we want to turn on those indications even when the screen is off.
+ */
+ void setRadioIndicationUpdateMode(int subId, int filters, int mode);
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 54e07a16..bb07363 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,7 +19,6 @@
java_library {
name: "android.test.mock",
- // Needs to be consistent with the repackaged version of this make target.
java_version: "1.8",
srcs: ["src/**/*.java"],
@@ -28,15 +27,3 @@
"framework",
],
}
-
-// Build the repackaged.android.test.mock library
-// ==============================================
-java_library_static {
- name: "repackaged.android.test.mock",
-
- static_libs: ["android.test.mock"],
-
- jarjar_rules: "jarjar-rules.txt",
- // Pin java_version until jarjar is certified to support later versions. http://b/72703434
- java_version: "1.8",
-}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index c69279b..1cce2c3 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -24,7 +24,7 @@
srcs: ["src/**/*.java"],
errorprone: {
- javacflags: ["-Xep:DepAnn:ERROR"],
+ javacflags: ["-Xep:DepAnn:ERROR"],
},
sdk_version: "current",
@@ -56,8 +56,21 @@
java_library_static {
name: "repackaged.android.test.runner",
+ srcs: ["src/**/*.java"],
+ exclude_srcs: [
+ "src/android/test/ActivityUnitTestCase.java",
+ "src/android/test/ApplicationTestCase.java",
+ "src/android/test/IsolatedContext.java",
+ "src/android/test/ProviderTestCase.java",
+ "src/android/test/ProviderTestCase2.java",
+ "src/android/test/RenamingDelegatingContext.java",
+ "src/android/test/ServiceTestCase.java",
+ ],
+
sdk_version: "current",
- static_libs: ["android.test.runner"],
+ libs: [
+ "android.test.base",
+ ],
jarjar_rules: "jarjar-rules.txt",
// Pin java_version until jarjar is certified to support later versions. http://b/72703434
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
index 1a4f6d5..f97d1c9 100644
--- a/test-runner/tests/Android.mk
+++ b/test-runner/tests/Android.mk
@@ -32,6 +32,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests
+# Because of android.test.mock.
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/AccessibilityEventsLogger/Android.mk b/tests/AccessibilityEventsLogger/Android.mk
index 52bc579..4224017 100644
--- a/tests/AccessibilityEventsLogger/Android.mk
+++ b/tests/AccessibilityEventsLogger/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := AccessibilityEventsLogger
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk
index 767e899..33d15d2 100644
--- a/tests/ActivityManagerPerfTests/test-app/Android.mk
+++ b/tests/ActivityManagerPerfTests/test-app/Android.mk
@@ -26,5 +26,6 @@
LOCAL_MIN_SDK_VERSION := 25
LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp
+LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk
index 7597e69..f23a665 100644
--- a/tests/ActivityManagerPerfTests/tests/Android.mk
+++ b/tests/ActivityManagerPerfTests/tests/Android.mk
@@ -26,6 +26,7 @@
ActivityManagerPerfTestsUtils
LOCAL_PACKAGE_NAME := ActivityManagerPerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MIN_SDK_VERSION := 25
diff --git a/tests/ActivityManagerPerfTests/utils/Android.mk b/tests/ActivityManagerPerfTests/utils/Android.mk
index 7276e37..60c9423 100644
--- a/tests/ActivityManagerPerfTests/utils/Android.mk
+++ b/tests/ActivityManagerPerfTests/utils/Android.mk
@@ -16,6 +16,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
diff --git a/tests/ActivityTests/Android.mk b/tests/ActivityTests/Android.mk
index f3c6b5a..274fc5f 100644
--- a/tests/ActivityTests/Android.mk
+++ b/tests/ActivityTests/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := ActivityTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index 917293f..1fb548b 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := AppLaunch
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
diff --git a/tests/Assist/Android.mk b/tests/Assist/Android.mk
index f31c4dd..d0d3eca 100644
--- a/tests/Assist/Android.mk
+++ b/tests/Assist/Android.mk
@@ -6,5 +6,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Assist
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
index da1a08b..b10305d 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
+++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
@@ -27,6 +27,7 @@
android-support-test \
LOCAL_PACKAGE_NAME := BackgroundDexOptServiceIntegrationTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
diff --git a/tests/BandwidthTests/Android.mk b/tests/BandwidthTests/Android.mk
index 7bc5f857..d00fdc6 100644
--- a/tests/BandwidthTests/Android.mk
+++ b/tests/BandwidthTests/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := BandwidthEnforcementTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_PACKAGE)
diff --git a/tests/BatteryWaster/Android.mk b/tests/BatteryWaster/Android.mk
index 6db34a7..fb244a8 100644
--- a/tests/BatteryWaster/Android.mk
+++ b/tests/BatteryWaster/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BatteryWaster
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/BiDiTests/Android.mk b/tests/BiDiTests/Android.mk
index ae29fc2..78cf4be 100644
--- a/tests/BiDiTests/Android.mk
+++ b/tests/BiDiTests/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := BiDiTests
+LOCAL_SDK_VERSION := current
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk
index 5765575..0934889 100644
--- a/tests/BrowserPowerTest/Android.mk
+++ b/tests/BrowserPowerTest/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := BrowserPowerTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
#LOCAL_INSTRUMENTATION_FOR := browserpowertest
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
index 9e7f618..a900077 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
# LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PACKAGE_NAME := SmartCamera-tests
diff --git a/tests/CameraPrewarmTest/Android.mk b/tests/CameraPrewarmTest/Android.mk
index b6316f0..e128504 100644
--- a/tests/CameraPrewarmTest/Android.mk
+++ b/tests/CameraPrewarmTest/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := CameraPrewarmTest
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
index b071ec4..6a0a93e 100644
--- a/tests/CanvasCompare/Android.mk
+++ b/tests/CanvasCompare/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
LOCAL_PACKAGE_NAME := CanvasCompare
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/Compatibility/Android.mk b/tests/Compatibility/Android.mk
index 82e2126..9c47a26 100644
--- a/tests/Compatibility/Android.mk
+++ b/tests/Compatibility/Android.mk
@@ -24,6 +24,7 @@
LOCAL_PACKAGE_NAME := AppCompatibilityTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk
index 85f7edf..bcf3599 100644
--- a/tests/DataIdleTest/Android.mk
+++ b/tests/DataIdleTest/Android.mk
@@ -20,6 +20,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := DataIdleTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk
index 7187a37..ee2ec0a 100644
--- a/tests/DexLoggerIntegrationTests/Android.mk
+++ b/tests/DexLoggerIntegrationTests/Android.mk
@@ -35,6 +35,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
diff --git a/tests/DozeTest/Android.mk b/tests/DozeTest/Android.mk
index 01f10e5..ec250ff 100644
--- a/tests/DozeTest/Android.mk
+++ b/tests/DozeTest/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := DozeTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/DpiTest/Android.mk b/tests/DpiTest/Android.mk
index f55ce6e..e69d082 100644
--- a/tests/DpiTest/Android.mk
+++ b/tests/DpiTest/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := DensityTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/FeatureSplit/base/Android.mk b/tests/FeatureSplit/base/Android.mk
index 6da1b38..8646460 100644
--- a/tests/FeatureSplit/base/Android.mk
+++ b/tests/FeatureSplit/base/Android.mk
@@ -20,6 +20,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplitBase
+LOCAL_SDK_VERSION := current
LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index e6ba5c2..d4d2589 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -20,6 +20,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplit1
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_APK_LIBRARIES := FeatureSplitBase
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index c8e8609..5e5e78b 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -20,6 +20,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplit2
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_APK_LIBRARIES := FeatureSplitBase
diff --git a/tests/FixVibrateSetting/Android.mk b/tests/FixVibrateSetting/Android.mk
index 2a88e5a..86db09e 100644
--- a/tests/FixVibrateSetting/Android.mk
+++ b/tests/FixVibrateSetting/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FixVibrateSetting
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk
index 1873cc1..0664d4d 100644
--- a/tests/FrameworkPerf/Android.mk
+++ b/tests/FrameworkPerf/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FrameworkPerf
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_STATIC_JAVA_LIBRARIES := junit
diff --git a/tests/GridLayoutTest/Android.mk b/tests/GridLayoutTest/Android.mk
index a02918b..e7e3ccd 100644
--- a/tests/GridLayoutTest/Android.mk
+++ b/tests/GridLayoutTest/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := GridLayoutTest
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk
index 8550d70..cf1a512 100644
--- a/tests/HierarchyViewerTest/Android.mk
+++ b/tests/HierarchyViewerTest/Android.mk
@@ -6,8 +6,9 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := HierarchyViewerTest
+LOCAL_SDK_VERSION := current
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
LOCAL_STATIC_JAVA_LIBRARIES := junit
include $(BUILD_PACKAGE)
diff --git a/tests/HwAccelerationTest/Android.mk b/tests/HwAccelerationTest/Android.mk
index d4743f9..11ea954 100644
--- a/tests/HwAccelerationTest/Android.mk
+++ b/tests/HwAccelerationTest/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := HwAccelerationTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/ImfTest/Android.mk b/tests/ImfTest/Android.mk
index eb5327b..a8f5b08 100644
--- a/tests/ImfTest/Android.mk
+++ b/tests/ImfTest/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ImfTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk
index a0df959..14186d7 100644
--- a/tests/ImfTest/tests/Android.mk
+++ b/tests/ImfTest/tests/Android.mk
@@ -11,6 +11,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := ImfTestTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := ImfTest
diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk
index b69e3e4..da56696 100644
--- a/tests/Internal/Android.mk
+++ b/tests/Internal/Android.mk
@@ -18,6 +18,7 @@
LOCAL_CERTIFICATE := platform
LOCAL_PACKAGE_NAME := InternalTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_PACKAGE)
diff --git a/tests/JobSchedulerTestApp/Android.mk b/tests/JobSchedulerTestApp/Android.mk
index 7336d8c..48ee1f6 100644
--- a/tests/JobSchedulerTestApp/Android.mk
+++ b/tests/JobSchedulerTestApp/Android.mk
@@ -8,6 +8,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := JobSchedulerTestApp
+LOCAL_SDK_VERSION := current
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/LargeAssetTest/Android.mk b/tests/LargeAssetTest/Android.mk
index cb7f01b..f6d98bf 100644
--- a/tests/LargeAssetTest/Android.mk
+++ b/tests/LargeAssetTest/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LargeAssetTest
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/LegacyAssistant/Android.mk b/tests/LegacyAssistant/Android.mk
index 0ad48d1..a583369 100644
--- a/tests/LegacyAssistant/Android.mk
+++ b/tests/LegacyAssistant/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LegacyAssistant
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
diff --git a/tests/LocationTracker/Android.mk b/tests/LocationTracker/Android.mk
index b142d22..0d51b3b 100644
--- a/tests/LocationTracker/Android.mk
+++ b/tests/LocationTracker/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LocationTracker
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk
index ed58643..a693eaa 100644
--- a/tests/LockTaskTests/Android.mk
+++ b/tests/LockTaskTests/Android.mk
@@ -5,6 +5,7 @@
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
LOCAL_PACKAGE_NAME := LockTaskTests
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-Iaidl-files-under, src) $(call all-java-files-under, src)
diff --git a/tests/LotsOfApps/Android.mk b/tests/LotsOfApps/Android.mk
index 0ef9550..bee3bcc 100644
--- a/tests/LotsOfApps/Android.mk
+++ b/tests/LotsOfApps/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LotsOfApps
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/LowStorageTest/Android.mk b/tests/LowStorageTest/Android.mk
index ab5b9e9..bdde6bd 100644
--- a/tests/LowStorageTest/Android.mk
+++ b/tests/LowStorageTest/Android.mk
@@ -21,5 +21,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := lowstoragetest
+LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
index e186e9f..5040d5a 100644
--- a/tests/MemoryUsage/Android.mk
+++ b/tests/MemoryUsage/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := MemoryUsage
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index 6fb6025..fe65ecc 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -17,5 +17,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := NetworkSecurityConfigTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/OneMedia/Android.mk b/tests/OneMedia/Android.mk
index 9fc6403..41f3f64 100644
--- a/tests/OneMedia/Android.mk
+++ b/tests/OneMedia/Android.mk
@@ -7,6 +7,7 @@
$(call all-Iaidl-files-under, src)
LOCAL_PACKAGE_NAME := OneMedia
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
diff --git a/tests/RemoteDisplayProvider/Android.mk b/tests/RemoteDisplayProvider/Android.mk
index 2f4b343..e827ec2 100644
--- a/tests/RemoteDisplayProvider/Android.mk
+++ b/tests/RemoteDisplayProvider/Android.mk
@@ -21,6 +21,6 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
-LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay
+LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay.stubs
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/RenderThreadTest/Android.mk b/tests/RenderThreadTest/Android.mk
index e07e943..4e5f35b 100644
--- a/tests/RenderThreadTest/Android.mk
+++ b/tests/RenderThreadTest/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := RenderThreadTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES += android-common
diff --git a/tests/SerialChat/Android.mk b/tests/SerialChat/Android.mk
index a534e1a..ed6ca999 100644
--- a/tests/SerialChat/Android.mk
+++ b/tests/SerialChat/Android.mk
@@ -22,5 +22,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SerialChat
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/ServiceCrashTest/Android.mk b/tests/ServiceCrashTest/Android.mk
index f7b3452..d1f6450 100644
--- a/tests/ServiceCrashTest/Android.mk
+++ b/tests/ServiceCrashTest/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ServiceCrashTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.base
diff --git a/tests/SharedLibrary/client/Android.mk b/tests/SharedLibrary/client/Android.mk
index a04fb05..9e76c40 100644
--- a/tests/SharedLibrary/client/Android.mk
+++ b/tests/SharedLibrary/client/Android.mk
@@ -7,6 +7,7 @@
LOCAL_RES_LIBRARIES := SharedLibrary
LOCAL_PACKAGE_NAME := SharedLibraryClient
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/SharedLibrary/lib/Android.mk b/tests/SharedLibrary/lib/Android.mk
index 78fcb8b..3c1ca87 100644
--- a/tests/SharedLibrary/lib/Android.mk
+++ b/tests/SharedLibrary/lib/Android.mk
@@ -6,6 +6,7 @@
LOCAL_AAPT_FLAGS := --shared-lib
LOCAL_PACKAGE_NAME := SharedLibrary
+LOCAL_SDK_VERSION := current
LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_PRIVILEGED_MODULE := true
diff --git a/tests/ShowWhenLockedApp/Android.mk b/tests/ShowWhenLockedApp/Android.mk
index 0064167..41e0ac4 100644
--- a/tests/ShowWhenLockedApp/Android.mk
+++ b/tests/ShowWhenLockedApp/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := ShowWhenLocked
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/SmokeTestApps/Android.mk b/tests/SmokeTestApps/Android.mk
index 3f5f011..1f564e0 100644
--- a/tests/SmokeTestApps/Android.mk
+++ b/tests/SmokeTestApps/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := SmokeTestTriggerApps
+LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTestApp/Android.mk b/tests/SoundTriggerTestApp/Android.mk
index c327b09..73fb5e8 100644
--- a/tests/SoundTriggerTestApp/Android.mk
+++ b/tests/SoundTriggerTestApp/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SoundTriggerTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := optional
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
index 030d5f4..204a74e 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -31,5 +31,6 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := SoundTriggerTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk
index b068bef..4d15b2d 100644
--- a/tests/Split/Android.mk
+++ b/tests/Split/Android.mk
@@ -19,6 +19,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Split
+LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_SPLITS := mdpi-v4 hdpi-v4 xhdpi-v4 xxhdpi-v4
diff --git a/tests/StatusBar/Android.mk b/tests/StatusBar/Android.mk
index 502657f..e845335 100644
--- a/tests/StatusBar/Android.mk
+++ b/tests/StatusBar/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := StatusBarTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/SystemUIDemoModeController/Android.mk b/tests/SystemUIDemoModeController/Android.mk
index 64ea63c..cc6fa8d 100644
--- a/tests/SystemUIDemoModeController/Android.mk
+++ b/tests/SystemUIDemoModeController/Android.mk
@@ -6,5 +6,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := DemoModeController
+LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/TouchLatency/Android.mk b/tests/TouchLatency/Android.mk
index 969283d..2334bd8 100644
--- a/tests/TouchLatency/Android.mk
+++ b/tests/TouchLatency/Android.mk
@@ -15,6 +15,7 @@
--auto-add-overlay
LOCAL_PACKAGE_NAME := TouchLatency
+LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/tests/TransformTest/Android.mk b/tests/TransformTest/Android.mk
index 2d3637d..5340cdd 100644
--- a/tests/TransformTest/Android.mk
+++ b/tests/TransformTest/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := TransformTest
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/TransitionTests/Android.mk b/tests/TransitionTests/Android.mk
index 22fa638..a696156 100644
--- a/tests/TransitionTests/Android.mk
+++ b/tests/TransitionTests/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := TransitionTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES += android-common
diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk
index 2fa1950..116cc0a 100644
--- a/tests/TtsTests/Android.mk
+++ b/tests/TtsTests/Android.mk
@@ -24,5 +24,6 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := TtsTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk
index 6b5c999..6735c7c 100644
--- a/tests/UsageStatsTest/Android.mk
+++ b/tests/UsageStatsTest/Android.mk
@@ -11,5 +11,6 @@
LOCAL_CERTIFICATE := platform
LOCAL_PACKAGE_NAME := UsageStatsTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk
index 3137a73..cd7aaed 100644
--- a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk
@@ -25,6 +25,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := AoapTestDeviceApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk
index 354e8c9..bd8a51b 100644
--- a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk
@@ -25,6 +25,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := AoapTestHostApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk
index 2d6d6ea8..fed454e 100644
--- a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk
@@ -25,6 +25,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := UsbHostExternalManagementTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PRIVILEGED_MODULE := true
# TODO remove tests tag
diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk
index a04f32a..4e215cc 100644
--- a/tests/UsbTests/Android.mk
+++ b/tests/UsbTests/Android.mk
@@ -38,6 +38,7 @@
LOCAL_CERTIFICATE := platform
LOCAL_PACKAGE_NAME := UsbTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/tests/UsesFeature2Test/Android.mk b/tests/UsesFeature2Test/Android.mk
index cc784d7..4cba4ff 100644
--- a/tests/UsesFeature2Test/Android.mk
+++ b/tests/UsesFeature2Test/Android.mk
@@ -19,6 +19,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := UsesFeature2Test
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
diff --git a/tests/VectorDrawableTest/Android.mk b/tests/VectorDrawableTest/Android.mk
index dd8a4d4..155b2bc 100644
--- a/tests/VectorDrawableTest/Android.mk
+++ b/tests/VectorDrawableTest/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := VectorDrawableTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/VoiceEnrollment/Android.mk b/tests/VoiceEnrollment/Android.mk
index 2ab3d02..725e2bd 100644
--- a/tests/VoiceEnrollment/Android.mk
+++ b/tests/VoiceEnrollment/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := VoiceEnrollment
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := optional
diff --git a/tests/VoiceInteraction/Android.mk b/tests/VoiceInteraction/Android.mk
index 8decca7..aa48b42 100644
--- a/tests/VoiceInteraction/Android.mk
+++ b/tests/VoiceInteraction/Android.mk
@@ -6,5 +6,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := VoiceInteraction
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/WallpaperTest/Android.mk b/tests/WallpaperTest/Android.mk
index b4259cd..4815500 100644
--- a/tests/WallpaperTest/Android.mk
+++ b/tests/WallpaperTest/Android.mk
@@ -8,6 +8,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := WallpaperTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/WindowManagerStressTest/Android.mk b/tests/WindowManagerStressTest/Android.mk
index e4cbe93..6f4403f 100644
--- a/tests/WindowManagerStressTest/Android.mk
+++ b/tests/WindowManagerStressTest/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := WindowManagerStressTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/appwidgets/AppWidgetHostTest/Android.mk b/tests/appwidgets/AppWidgetHostTest/Android.mk
index 4d0c704..c9e6c6b 100644
--- a/tests/appwidgets/AppWidgetHostTest/Android.mk
+++ b/tests/appwidgets/AppWidgetHostTest/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := AppWidgetHostTest
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/appwidgets/AppWidgetProviderTest/Android.mk b/tests/appwidgets/AppWidgetProviderTest/Android.mk
index 6084fb9..b26c60b 100644
--- a/tests/appwidgets/AppWidgetProviderTest/Android.mk
+++ b/tests/appwidgets/AppWidgetProviderTest/Android.mk
@@ -6,6 +6,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := AppWidgetProvider
+LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index 202a699..e9618300 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -40,6 +40,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BackupTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 2e1519b..4227da6 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -22,6 +22,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -252,6 +253,19 @@
assertEqualsThroughMarshalling(netCap);
}
+ @Test
+ public void testOemPaid() {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.maybeMarkCapabilitiesRestricted();
+ assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
+ assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ nc.addCapability(NET_CAPABILITY_OEM_PAID);
+ nc.maybeMarkCapabilitiesRestricted();
+ assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ }
+
private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
Parcel p = Parcel.obtain();
netCap.writeToParcel(p, /* flags */ 0);
diff --git a/tests/permission/Android.mk b/tests/permission/Android.mk
index 7f81d9a..dd2f3ec 100644
--- a/tests/permission/Android.mk
+++ b/tests/permission/Android.mk
@@ -10,6 +10,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_PACKAGE_NAME := FrameworkPermissionTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk
index 3c80ad8..9795188 100644
--- a/tests/privapp-permissions/Android.mk
+++ b/tests/privapp-permissions/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := PrivAppPermissionTest
+LOCAL_SDK_VERSION := current
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
LOCAL_REQUIRED_MODULES := privapp-permissions-test.xml
@@ -16,6 +17,7 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := VendorPrivAppPermissionTest
+LOCAL_SDK_VERSION := current
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
LOCAL_VENDOR_MODULE := true
@@ -31,6 +33,7 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest
+LOCAL_SDK_VERSION := current
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MANIFEST_FILE := product/AndroidManifest.xml
LOCAL_PRODUCT_MODULE := true
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
index f9b3ce4..3b0221c 100644
--- a/tests/testables/tests/Android.mk
+++ b/tests/testables/tests/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := TestablesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-Iaidl-files-under, src)
diff --git a/tests/utils/DummyIME/Android.mk b/tests/utils/DummyIME/Android.mk
index c8d9f87..0f6c988 100644
--- a/tests/utils/DummyIME/Android.mk
+++ b/tests/utils/DummyIME/Android.mk
@@ -22,5 +22,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := DummyIME
+LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 7cffeea..1b6f882 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -26,11 +26,14 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
+#include "text/Utf8Iterator.h"
#include "util/ImmutableMap.h"
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
+using ::aapt::ResourceUtils::StringBuilder;
+using ::aapt::text::Utf8Iterator;
using ::android::StringPiece;
namespace aapt {
@@ -169,114 +172,212 @@
config_(config),
options_(options) {}
-/**
- * Build a string from XML that converts nested elements into Span objects.
- */
+// Base class Node for representing the various Spans and UntranslatableSections of an XML string.
+// This will be used to traverse and flatten the XML string into a single std::string, with all
+// Span and Untranslatable data maintained in parallel, as indices into the string.
+class Node {
+ public:
+ virtual ~Node() = default;
+
+ // Adds the given child node to this parent node's set of child nodes, moving ownership to the
+ // parent node as well.
+ // Returns a pointer to the child node that was added as a convenience.
+ template <typename T>
+ T* AddChild(std::unique_ptr<T> node) {
+ T* raw_ptr = node.get();
+ children.push_back(std::move(node));
+ return raw_ptr;
+ }
+
+ virtual void Build(StringBuilder* builder) const {
+ for (const auto& child : children) {
+ child->Build(builder);
+ }
+ }
+
+ std::vector<std::unique_ptr<Node>> children;
+};
+
+// A chunk of text in the XML string. This lives between other tags, such as XLIFF tags and Spans.
+class SegmentNode : public Node {
+ public:
+ std::string data;
+
+ void Build(StringBuilder* builder) const override {
+ builder->AppendText(data);
+ }
+};
+
+// A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
+class SpanNode : public Node {
+ public:
+ std::string name;
+
+ void Build(StringBuilder* builder) const override {
+ StringBuilder::SpanHandle span_handle = builder->StartSpan(name);
+ Node::Build(builder);
+ builder->EndSpan(span_handle);
+ }
+};
+
+// An XLIFF 'g' tag, which marks a section of the string as untranslatable.
+class UntranslatableNode : public Node {
+ public:
+ void Build(StringBuilder* builder) const override {
+ StringBuilder::UntranslatableHandle handle = builder->StartUntranslatable();
+ Node::Build(builder);
+ builder->EndUntranslatable(handle);
+ }
+};
+
+// Build a string from XML that converts nested elements into Span objects.
bool ResourceParser::FlattenXmlSubtree(
xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
std::vector<UntranslatableSection>* out_untranslatable_sections) {
- // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
- // The stack elements refer to the indices in out_style_string->spans.
- // By first adding to the out_style_string->spans vector, and then using the stack to refer
- // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
- std::vector<size_t> span_stack;
-
- // Clear the output variables.
- out_raw_string->clear();
- out_style_string->spans.clear();
- out_untranslatable_sections->clear();
-
- // The StringBuilder will concatenate the various segments of text which are initially
- // separated by tags. It also handles unicode escape codes and quotations.
- util::StringBuilder builder;
+ std::string raw_string;
+ std::string current_text;
// The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
Maybe<size_t> untranslatable_start_depth;
+ Node root;
+ std::vector<Node*> node_stack;
+ node_stack.push_back(&root);
+
+ bool saw_span_node = false;
+ SegmentNode* first_segment = nullptr;
+ SegmentNode* last_segment = nullptr;
+
size_t depth = 1;
- while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
+ while (depth > 0 && xml::XmlPullParser::IsGoodEvent(parser->Next())) {
const xml::XmlPullParser::Event event = parser->event();
- if (event == xml::XmlPullParser::Event::kStartElement) {
- if (parser->element_namespace().empty()) {
- // This is an HTML tag which we encode as a span. Add it to the span stack.
- std::string span_name = parser->element_name();
- const auto end_attr_iter = parser->end_attributes();
- for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter; ++attr_iter) {
- span_name += ";";
- span_name += attr_iter->name;
- span_name += "=";
- span_name += attr_iter->value;
+ // First take care of any SegmentNodes that should be created.
+ if (event == xml::XmlPullParser::Event::kStartElement ||
+ event == xml::XmlPullParser::Event::kEndElement) {
+ if (!current_text.empty()) {
+ std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+ segment_node->data = std::move(current_text);
+ last_segment = node_stack.back()->AddChild(std::move(segment_node));
+ if (first_segment == nullptr) {
+ first_segment = last_segment;
}
+ current_text = {};
+ }
+ }
- // Make sure the string is representable in our binary format.
- if (builder.Utf16Len() > std::numeric_limits<uint32_t>::max()) {
- diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
- << "style string '" << builder.ToString() << "' is too long");
- return false;
- }
+ switch (event) {
+ case xml::XmlPullParser::Event::kText: {
+ current_text += parser->text();
+ raw_string += parser->text();
+ } break;
- out_style_string->spans.push_back(
- Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
- span_stack.push_back(out_style_string->spans.size() - 1);
- } else if (parser->element_namespace() == sXliffNamespaceUri) {
- if (parser->element_name() == "g") {
- if (untranslatable_start_depth) {
- // We've already encountered an <xliff:g> tag, and nested <xliff:g> tags are illegal.
- diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
- << "illegal nested XLIFF 'g' tag");
- return false;
- } else {
- // Mark the start of an untranslatable section. Use UTF8 indices/lengths.
- untranslatable_start_depth = depth;
- const size_t current_idx = builder.ToString().size();
- out_untranslatable_sections->push_back(UntranslatableSection{current_idx, current_idx});
+ case xml::XmlPullParser::Event::kStartElement: {
+ if (parser->element_namespace().empty()) {
+ // This is an HTML tag which we encode as a span. Add it to the span stack.
+ std::unique_ptr<SpanNode> span_node = util::make_unique<SpanNode>();
+ span_node->name = parser->element_name();
+ const auto end_attr_iter = parser->end_attributes();
+ for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter;
+ ++attr_iter) {
+ span_node->name += ";";
+ span_node->name += attr_iter->name;
+ span_node->name += "=";
+ span_node->name += attr_iter->value;
}
+
+ node_stack.push_back(node_stack.back()->AddChild(std::move(span_node)));
+ saw_span_node = true;
+ } else if (parser->element_namespace() == sXliffNamespaceUri) {
+ // This is an XLIFF tag, which is not encoded as a span.
+ if (parser->element_name() == "g") {
+ // Check that an 'untranslatable' tag is not already being processed. Nested
+ // <xliff:g> tags are illegal.
+ if (untranslatable_start_depth) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "illegal nested XLIFF 'g' tag");
+ return false;
+ } else {
+ // Mark the beginning of an 'untranslatable' section.
+ untranslatable_start_depth = depth;
+ node_stack.push_back(
+ node_stack.back()->AddChild(util::make_unique<UntranslatableNode>()));
+ }
+ } else {
+ // Ignore unknown XLIFF tags, but don't warn.
+ node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
+ }
+ } else {
+ // Besides XLIFF, any other namespaced tag is unsupported and ignored.
+ diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
+ << "ignoring element '" << parser->element_name()
+ << "' with unknown namespace '" << parser->element_namespace() << "'");
+ node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
}
- // Ignore other xliff tags, they get handled by other tools.
- } else {
- // Besides XLIFF, any other namespaced tag is unsupported and ignored.
- diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
- << "ignoring element '" << parser->element_name()
- << "' with unknown namespace '" << parser->element_namespace() << "'");
- }
+ // Enter one level inside the element.
+ depth++;
+ } break;
- // Enter one level inside the element.
- depth++;
- } else if (event == xml::XmlPullParser::Event::kText) {
- // Record both the raw text and append to the builder to deal with escape sequences
- // and quotations.
- out_raw_string->append(parser->text());
- builder.Append(parser->text());
- } else if (event == xml::XmlPullParser::Event::kEndElement) {
- // Return one level from within the element.
- depth--;
- if (depth == 0) {
+ case xml::XmlPullParser::Event::kEndElement: {
+ // Return one level from within the element.
+ depth--;
+ if (depth == 0) {
+ break;
+ }
+
+ node_stack.pop_back();
+ if (untranslatable_start_depth == make_value(depth)) {
+ // This is the end of an untranslatable section.
+ untranslatable_start_depth = {};
+ }
+ } break;
+
+ default:
+ // ignore.
break;
- }
-
- if (parser->element_namespace().empty()) {
- // This is an HTML tag which we encode as a span. Update the span
- // stack and pop the top entry.
- Span& top_span = out_style_string->spans[span_stack.back()];
- top_span.last_char = builder.Utf16Len() - 1;
- span_stack.pop_back();
- } else if (untranslatable_start_depth == make_value(depth)) {
- // This is the end of an untranslatable section. Use UTF8 indices/lengths.
- UntranslatableSection& untranslatable_section = out_untranslatable_sections->back();
- untranslatable_section.end = builder.ToString().size();
- untranslatable_start_depth = {};
- }
- } else if (event == xml::XmlPullParser::Event::kComment) {
- // Ignore.
- } else {
- LOG(FATAL) << "unhandled XML event";
}
}
- CHECK(span_stack.empty()) << "spans haven't been fully processed";
- out_style_string->str = builder.ToString();
+ // Sanity check to make sure we processed all the nodes.
+ CHECK(node_stack.size() == 1u);
+ CHECK(node_stack.back() == &root);
+
+ if (!saw_span_node) {
+ // If there were no spans, we must treat this string a little differently (according to AAPT).
+ // Find and strip the leading whitespace from the first segment, and the trailing whitespace
+ // from the last segment.
+ if (first_segment != nullptr) {
+ // Trim leading whitespace.
+ StringPiece trimmed = util::TrimLeadingWhitespace(first_segment->data);
+ if (trimmed.size() != first_segment->data.size()) {
+ first_segment->data = trimmed.to_string();
+ }
+ }
+
+ if (last_segment != nullptr) {
+ // Trim trailing whitespace.
+ StringPiece trimmed = util::TrimTrailingWhitespace(last_segment->data);
+ if (trimmed.size() != last_segment->data.size()) {
+ last_segment->data = trimmed.to_string();
+ }
+ }
+ }
+
+ // Have the XML structure flatten itself into the StringBuilder. The StringBuilder will take
+ // care of recording the correctly adjusted Spans and UntranslatableSections.
+ StringBuilder builder;
+ root.Build(&builder);
+ if (!builder) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) << builder.GetError());
+ return false;
+ }
+
+ ResourceUtils::FlattenedXmlString flattened_string = builder.GetFlattenedString();
+ *out_raw_string = std::move(raw_string);
+ *out_untranslatable_sections = std::move(flattened_string.untranslatable_sections);
+ out_style_string->str = std::move(flattened_string.text);
+ out_style_string->spans = std::move(flattened_string.spans);
return true;
}
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 618c8ed..c98c0b9 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -95,6 +95,16 @@
ASSERT_THAT(str, NotNull());
EXPECT_THAT(*str, StrValueEq(" hey there "));
EXPECT_THAT(str->untranslatable_sections, IsEmpty());
+
+ ASSERT_TRUE(TestParse(R"(<string name="bar">Isn\'t it cool?</string>)"));
+ str = test::GetValue<String>(&table_, "string/bar");
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("Isn't it cool?"));
+
+ ASSERT_TRUE(TestParse(R"(<string name="baz">"Isn't it cool?"</string>)"));
+ str = test::GetValue<String>(&table_, "string/baz");
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("Isn't it cool?"));
}
TEST_F(ResourceParserTest, ParseEscapedString) {
@@ -126,16 +136,16 @@
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
ASSERT_THAT(str, NotNull());
- EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string"));
+ EXPECT_THAT(str->value->value, StrEq("This is my aunt\u2019s fickle string"));
EXPECT_THAT(str->value->spans, SizeIs(2));
EXPECT_THAT(str->untranslatable_sections, IsEmpty());
- EXPECT_THAT(*str->value->spans[0].name, Eq("b"));
- EXPECT_THAT(str->value->spans[0].first_char, Eq(17u));
+ EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+ EXPECT_THAT(str->value->spans[0].first_char, Eq(18u));
EXPECT_THAT(str->value->spans[0].last_char, Eq(30u));
- EXPECT_THAT(*str->value->spans[1].name, Eq("small"));
- EXPECT_THAT(str->value->spans[1].first_char, Eq(24u));
+ EXPECT_THAT(*str->value->spans[1].name, StrEq("small"));
+ EXPECT_THAT(str->value->spans[1].first_char, Eq(25u));
EXPECT_THAT(str->value->spans[1].last_char, Eq(30u));
}
@@ -144,7 +154,7 @@
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_THAT(str, NotNull());
- EXPECT_THAT(*str->value, Eq("This is what I think"));
+ EXPECT_THAT(*str->value, StrEq("This is what I think"));
EXPECT_THAT(str->untranslatable_sections, IsEmpty());
ASSERT_TRUE(TestParse(R"(<string name="foo2">" This is what I think "</string>)"));
@@ -154,6 +164,25 @@
EXPECT_THAT(*str, StrValueEq(" This is what I think "));
}
+TEST_F(ResourceParserTest, ParseStyledStringWithWhitespace) {
+ std::string input = R"(<string name="foo"> <b> My <i> favorite</i> string </b> </string>)";
+ ASSERT_TRUE(TestParse(input));
+
+ StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(str->value->value, StrEq(" My favorite string "));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
+
+ ASSERT_THAT(str->value->spans, SizeIs(2u));
+ EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+ EXPECT_THAT(str->value->spans[0].first_char, Eq(1u));
+ EXPECT_THAT(str->value->spans[0].last_char, Eq(21u));
+
+ EXPECT_THAT(*str->value->spans[1].name, StrEq("i"));
+ EXPECT_THAT(str->value->spans[1].first_char, Eq(5u));
+ EXPECT_THAT(str->value->spans[1].last_char, Eq(13u));
+}
+
TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
@@ -182,12 +211,9 @@
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_THAT(str, NotNull());
EXPECT_THAT(*str, StrValueEq("There are %1$d apples"));
- ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
- // We expect indices and lengths that span to include the whitespace
- // before %1$d. This is due to how the StringBuilder withholds whitespace unless
- // needed (to deal with line breaks, etc.).
- EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
+ ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+ EXPECT_THAT(str->untranslatable_sections[0].start, Eq(10u));
EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
}
@@ -199,14 +225,16 @@
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
ASSERT_THAT(str, NotNull());
- EXPECT_THAT(str->value->value, Eq("There are %1$d apples"));
- ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+ EXPECT_THAT(str->value->value, Eq(" There are %1$d apples"));
- // We expect indices and lengths that span to include the whitespace
- // before %1$d. This is due to how the StringBuilder withholds whitespace unless
- // needed (to deal with line breaks, etc.).
- EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
- EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
+ ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+ EXPECT_THAT(str->untranslatable_sections[0].start, Eq(11u));
+ EXPECT_THAT(str->untranslatable_sections[0].end, Eq(15u));
+
+ ASSERT_THAT(str->value->spans, SizeIs(1u));
+ EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+ EXPECT_THAT(str->value->spans[0].first_char, Eq(11u));
+ EXPECT_THAT(str->value->spans[0].last_char, Eq(14u));
}
TEST_F(ResourceParserTest, ParseNull) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 628466d..8fc3d65 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -18,17 +18,23 @@
#include <sstream>
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
#include "NameMangler.h"
#include "SdkConstants.h"
#include "format/binary/ResourceTypeExtensions.h"
+#include "text/Unicode.h"
+#include "text/Utf8Iterator.h"
#include "util/Files.h"
#include "util/Util.h"
+using ::aapt::text::IsWhitespace;
+using ::aapt::text::Utf8Iterator;
using ::android::StringPiece;
using ::android::StringPiece16;
+using ::android::base::StringPrintf;
namespace aapt {
namespace ResourceUtils {
@@ -750,5 +756,195 @@
return util::make_unique<BinaryPrimitive>(res_value);
}
+// Converts the codepoint to UTF-8 and appends it to the string.
+static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
+ ssize_t len = utf32_to_utf8_length(&codepoint, 1);
+ if (len < 0) {
+ return false;
+ }
+
+ const size_t start_append_pos = output->size();
+
+ // Make room for the next character.
+ output->resize(output->size() + len);
+
+ char* dst = &*(output->begin() + start_append_pos);
+ utf32_to_utf8(&codepoint, 1, dst, len + 1);
+ return true;
+}
+
+// Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
+// Unicode codepoint represented by the escape sequence to the string.
+static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
+ char32_t code = 0;
+ for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
+ char32_t codepoint = iter->Next();
+ char32_t a;
+ if (codepoint >= U'0' && codepoint <= U'9') {
+ a = codepoint - U'0';
+ } else if (codepoint >= U'a' && codepoint <= U'f') {
+ a = codepoint - U'a' + 10;
+ } else if (codepoint >= U'A' && codepoint <= U'F') {
+ a = codepoint - U'A' + 10;
+ } else {
+ return {};
+ }
+ code = (code << 4) | a;
+ }
+ return AppendCodepointToUtf8String(code, output);
+}
+
+StringBuilder::StringBuilder(bool preserve_spaces)
+ : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
+}
+
+StringBuilder& StringBuilder::AppendText(const std::string& text) {
+ if (!error_.empty()) {
+ return *this;
+ }
+
+ const size_t previous_len = xml_string_.text.size();
+ Utf8Iterator iter(text);
+ while (iter.HasNext()) {
+ char32_t codepoint = iter.Next();
+ if (!quote_ && text::IsWhitespace(codepoint)) {
+ if (!last_codepoint_was_space_) {
+ // Emit a space if it's the first.
+ xml_string_.text += ' ';
+ last_codepoint_was_space_ = true;
+ }
+
+ // Keep eating spaces.
+ continue;
+ }
+
+ // This is not a space.
+ last_codepoint_was_space_ = false;
+
+ if (codepoint == U'\\') {
+ if (iter.HasNext()) {
+ codepoint = iter.Next();
+ switch (codepoint) {
+ case U't':
+ xml_string_.text += '\t';
+ break;
+
+ case U'n':
+ xml_string_.text += '\n';
+ break;
+
+ case U'#':
+ case U'@':
+ case U'?':
+ case U'"':
+ case U'\'':
+ case U'\\':
+ xml_string_.text += static_cast<char>(codepoint);
+ break;
+
+ case U'u':
+ if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
+ error_ =
+ StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
+ return *this;
+ }
+ break;
+
+ default:
+ // Ignore the escape character and just include the codepoint.
+ AppendCodepointToUtf8String(codepoint, &xml_string_.text);
+ break;
+ }
+ }
+ } else if (!preserve_spaces_ && codepoint == U'"') {
+ // Only toggle the quote state when we are not preserving spaces.
+ quote_ = !quote_;
+
+ } else if (!quote_ && codepoint == U'\'') {
+ // This should be escaped.
+ error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
+ return *this;
+
+ } else {
+ AppendCodepointToUtf8String(codepoint, &xml_string_.text);
+ }
+ }
+
+ // Accumulate the added string's UTF-16 length.
+ const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
+ const size_t utf8_length = xml_string_.text.size();
+ ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
+ if (len < 0) {
+ error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
+ return *this;
+ }
+
+ utf16_len_ += static_cast<uint32_t>(len);
+ return *this;
+}
+
+StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
+ if (!error_.empty()) {
+ return 0u;
+ }
+
+ // When we start a span, all state associated with whitespace truncation and quotation is ended.
+ ResetTextState();
+ Span span;
+ span.name = name;
+ span.first_char = span.last_char = utf16_len_;
+ xml_string_.spans.push_back(std::move(span));
+ return xml_string_.spans.size() - 1;
+}
+
+void StringBuilder::EndSpan(SpanHandle handle) {
+ if (!error_.empty()) {
+ return;
+ }
+
+ // When we end a span, all state associated with whitespace truncation and quotation is ended.
+ ResetTextState();
+ xml_string_.spans[handle].last_char = utf16_len_ - 1u;
+}
+
+StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
+ if (!error_.empty()) {
+ return 0u;
+ }
+
+ UntranslatableSection section;
+ section.start = section.end = xml_string_.text.size();
+ xml_string_.untranslatable_sections.push_back(section);
+ return xml_string_.untranslatable_sections.size() - 1;
+}
+
+void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
+ if (!error_.empty()) {
+ return;
+ }
+ xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
+}
+
+FlattenedXmlString StringBuilder::GetFlattenedString() const {
+ return xml_string_;
+}
+
+std::string StringBuilder::to_string() const {
+ return xml_string_.text;
+}
+
+StringBuilder::operator bool() const {
+ return error_.empty();
+}
+
+std::string StringBuilder::GetError() const {
+ return error_;
+}
+
+void StringBuilder::ResetTextState() {
+ quote_ = preserve_spaces_;
+ last_codepoint_was_space_ = false;
+}
+
} // namespace ResourceUtils
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f83d49e..7af2fe0 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -224,6 +224,95 @@
const android::Res_value& res_value,
StringPool* dst_pool);
+// A string flattened from an XML hierarchy, which maintains tags and untranslatable sections
+// in parallel data structures.
+struct FlattenedXmlString {
+ std::string text;
+ std::vector<UntranslatableSection> untranslatable_sections;
+ std::vector<Span> spans;
+};
+
+// Flattens an XML hierarchy into a FlattenedXmlString, formatting the text, escaping characters,
+// and removing whitespace, all while keeping the untranslatable sections and spans in sync with the
+// transformations.
+//
+// Specifically, the StringBuilder will handle escaped characters like \t, \n, \\, \', etc.
+// Single quotes *must* be escaped, unless within a pair of double-quotes.
+// Pairs of double-quotes disable whitespace stripping of the enclosed text.
+// Unicode escape codes (\u0049) are interpreted and the represented Unicode character is inserted.
+//
+// A NOTE ON WHITESPACE:
+//
+// When preserve_spaces is false, and when text is not enclosed within double-quotes,
+// StringBuilder replaces a series of whitespace with a single space character. This happens at the
+// start and end of the string as well, so leading and trailing whitespace is possible.
+//
+// When a Span is started or stopped, the whitespace counter is reset, meaning if whitespace
+// is encountered directly after the span, it will be emitted. This leads to situations like the
+// following: "This <b> is </b> spaced" -> "This is spaced". Without spans, this would be properly
+// compressed: "This is spaced" -> "This is spaced".
+//
+// Untranslatable sections do not have the same problem:
+// "This <xliff:g> is </xliff:g> not spaced" -> "This is not spaced".
+//
+// NOTE: This is all the way it is because AAPT1 did it this way. Maintaining backwards
+// compatibility is important.
+//
+class StringBuilder {
+ public:
+ using SpanHandle = size_t;
+ using UntranslatableHandle = size_t;
+
+ // Creates a StringBuilder. If preserve_spaces is true, whitespace removal is not performed, and
+ // single quotations can be used without escaping them.
+ explicit StringBuilder(bool preserve_spaces = false);
+
+ // Appends a chunk of text.
+ StringBuilder& AppendText(const std::string& text);
+
+ // Starts a Span (tag) with the given name. The name is expected to be of the form:
+ // "tag_name;attr1=value;attr2=value;"
+ // Which is how Spans are encoded in the ResStringPool.
+ // To end the span, pass back the SpanHandle received from this method to the EndSpan() method.
+ SpanHandle StartSpan(const std::string& name);
+
+ // Ends a Span (tag). Pass in the matching SpanHandle previously obtained from StartSpan().
+ void EndSpan(SpanHandle handle);
+
+ // Starts an Untranslatable section.
+ // To end the section, pass back the UntranslatableHandle received from this method to
+ // the EndUntranslatable() method.
+ UntranslatableHandle StartUntranslatable();
+
+ // Ends an Untranslatable section. Pass in the matching UntranslatableHandle previously obtained
+ // from StartUntranslatable().
+ void EndUntranslatable(UntranslatableHandle handle);
+
+ // Returns the flattened XML string, with all spans and untranslatable sections encoded as
+ // parallel data structures.
+ FlattenedXmlString GetFlattenedString() const;
+
+ // Returns just the flattened XML text, with no spans or untranslatable sections.
+ std::string to_string() const;
+
+ // Returns true if there was no error.
+ explicit operator bool() const;
+
+ std::string GetError() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringBuilder);
+
+ void ResetTextState();
+
+ std::string error_;
+ FlattenedXmlString xml_string_;
+ uint32_t utf16_len_ = 0u;
+ bool preserve_spaces_;
+ bool quote_;
+ bool last_codepoint_was_space_ = false;
+};
+
} // namespace ResourceUtils
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index cb786d3..11f3fa3 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -212,4 +212,48 @@
Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
}
+TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys ")
+ .AppendText(" this is so cool ")
+ .to_string(),
+ Eq(" hey guys this is so cool "));
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" \" wow, so many \t ")
+ .AppendText("spaces. \"what? ")
+ .to_string(),
+ Eq(" wow, so many \t spaces. what? "));
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" where \t ")
+ .AppendText(" \nis the pie?")
+ .to_string(),
+ Eq(" where is the pie?"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderEscaping) {
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText("hey guys\\n ")
+ .AppendText(" this \\t is so\\\\ cool")
+ .to_string(),
+ Eq("hey guys\n this \t is so\\ cool"));
+ EXPECT_THAT(ResourceUtils::StringBuilder().AppendText("\\@\\?\\#\\\\\\'").to_string(),
+ Eq("@?#\\\'"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderMisplacedQuote) {
+ ResourceUtils::StringBuilder builder;
+ EXPECT_FALSE(builder.AppendText("they're coming!"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderUnicodeCodes) {
+ EXPECT_THAT(ResourceUtils::StringBuilder().AppendText("\\u00AF\\u0AF0 woah").to_string(),
+ Eq("\u00AF\u0AF0 woah"));
+ EXPECT_FALSE(ResourceUtils::StringBuilder().AppendText("\\u00 yo"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
+ EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
+ Eq("\""));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index 067372b..781b9fe 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -25,6 +25,7 @@
#include "androidfw/ResourceTypes.h"
#include "utils/misc.h"
+#include "ResourceUtils.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
#include "format/binary/ChunkWriter.h"
@@ -33,6 +34,8 @@
using namespace android;
+using ::aapt::ResourceUtils::StringBuilder;
+
namespace aapt {
namespace {
@@ -89,9 +92,9 @@
ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
// Process plain strings to make sure they get properly escaped.
- util::StringBuilder builder;
- builder.Append(node->text);
- AddString(builder.ToString(), kLowPriority, &flat_text->data);
+ StringBuilder builder;
+ builder.AppendText(node->text);
+ AddString(builder.to_string(), kLowPriority, &flat_text->data);
writer.Finish();
}
@@ -272,7 +275,7 @@
// There is no compiled value, so treat the raw string as compiled, once it is processed to
// make sure escape sequences are properly interpreted.
processed_str =
- util::StringBuilder(true /*preserve_spaces*/).Append(xml_attr->value).ToString();
+ StringBuilder(true /*preserve_spaces*/).AppendText(xml_attr->value).to_string();
compiled_text = StringPiece(processed_str);
}
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index b8f8804..9aaaa69 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -30,6 +30,7 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
+using ::aapt::ResourceUtils::StringBuilder;
using ::android::StringPiece;
namespace aapt {
@@ -133,10 +134,11 @@
// If we could not parse as any specific type, try a basic STRING.
if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
- util::StringBuilder string_builder;
- string_builder.Append(*raw_string->value);
+ StringBuilder string_builder;
+ string_builder.AppendText(*raw_string->value);
if (string_builder) {
- transformed = util::make_unique<String>(string_pool_->MakeRef(string_builder.ToString()));
+ transformed =
+ util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
}
}
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index e42145d..d1c9ca1 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -76,6 +76,34 @@
return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
}
+StringPiece TrimLeadingWhitespace(const StringPiece& str) {
+ if (str.size() == 0 || str.data() == nullptr) {
+ return str;
+ }
+
+ const char* start = str.data();
+ const char* end = start + str.length();
+
+ while (start != end && isspace(*start)) {
+ start++;
+ }
+ return StringPiece(start, end - start);
+}
+
+StringPiece TrimTrailingWhitespace(const StringPiece& str) {
+ if (str.size() == 0 || str.data() == nullptr) {
+ return str;
+ }
+
+ const char* start = str.data();
+ const char* end = start + str.length();
+
+ while (end != start && isspace(*(end - 1))) {
+ end--;
+ }
+ return StringPiece(start, end - start);
+}
+
StringPiece TrimWhitespace(const StringPiece& str) {
if (str.size() == 0 || str.data() == nullptr) {
return str;
@@ -269,162 +297,6 @@
return true;
}
-static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
- ssize_t len = utf32_to_utf8_length(&codepoint, 1);
- if (len < 0) {
- return false;
- }
-
- const size_t start_append_pos = output->size();
-
- // Make room for the next character.
- output->resize(output->size() + len);
-
- char* dst = &*(output->begin() + start_append_pos);
- utf32_to_utf8(&codepoint, 1, dst, len + 1);
- return true;
-}
-
-static bool AppendUnicodeCodepoint(Utf8Iterator* iter, std::string* output) {
- char32_t code = 0;
- for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
- char32_t codepoint = iter->Next();
- char32_t a;
- if (codepoint >= U'0' && codepoint <= U'9') {
- a = codepoint - U'0';
- } else if (codepoint >= U'a' && codepoint <= U'f') {
- a = codepoint - U'a' + 10;
- } else if (codepoint >= U'A' && codepoint <= U'F') {
- a = codepoint - U'A' + 10;
- } else {
- return {};
- }
- code = (code << 4) | a;
- }
- return AppendCodepointToUtf8String(code, output);
-}
-
-static bool IsCodepointSpace(char32_t codepoint) {
- if (static_cast<uint32_t>(codepoint) & 0xffffff00u) {
- return false;
- }
- return isspace(static_cast<char>(codepoint));
-}
-
-StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces) {
-}
-
-StringBuilder& StringBuilder::Append(const StringPiece& str) {
- if (!error_.empty()) {
- return *this;
- }
-
- // Where the new data will be appended to.
- const size_t new_data_index = str_.size();
-
- Utf8Iterator iter(str);
- while (iter.HasNext()) {
- const char32_t codepoint = iter.Next();
-
- if (last_char_was_escape_) {
- switch (codepoint) {
- case U't':
- str_ += '\t';
- break;
-
- case U'n':
- str_ += '\n';
- break;
-
- case U'#':
- case U'@':
- case U'?':
- case U'"':
- case U'\'':
- case U'\\':
- str_ += static_cast<char>(codepoint);
- break;
-
- case U'u':
- if (!AppendUnicodeCodepoint(&iter, &str_)) {
- error_ = "invalid unicode escape sequence";
- return *this;
- }
- break;
-
- default:
- // Ignore the escape character and just include the codepoint.
- AppendCodepointToUtf8String(codepoint, &str_);
- break;
- }
- last_char_was_escape_ = false;
-
- } else if (!preserve_spaces_ && codepoint == U'"') {
- if (!quote_ && trailing_space_) {
- // We found an opening quote, and we have trailing space, so we should append that
- // space now.
- if (trailing_space_) {
- // We had trailing whitespace, so replace with a single space.
- if (!str_.empty()) {
- str_ += ' ';
- }
- trailing_space_ = false;
- }
- }
- quote_ = !quote_;
-
- } else if (!preserve_spaces_ && codepoint == U'\'' && !quote_) {
- // This should be escaped.
- error_ = "unescaped apostrophe";
- return *this;
-
- } else if (codepoint == U'\\') {
- // This is an escape sequence, convert to the real value.
- if (!quote_ && trailing_space_) {
- // We had trailing whitespace, so
- // replace with a single space.
- if (!str_.empty()) {
- str_ += ' ';
- }
- trailing_space_ = false;
- }
- last_char_was_escape_ = true;
- } else {
- if (preserve_spaces_ || quote_) {
- // Quotes mean everything is taken, including whitespace.
- AppendCodepointToUtf8String(codepoint, &str_);
- } else {
- // This is not quoted text, so we will accumulate whitespace and only emit a single
- // character of whitespace if it is followed by a non-whitespace character.
- if (IsCodepointSpace(codepoint)) {
- // We found whitespace.
- trailing_space_ = true;
- } else {
- if (trailing_space_) {
- // We saw trailing space before, so replace all
- // that trailing space with one space.
- if (!str_.empty()) {
- str_ += ' ';
- }
- trailing_space_ = false;
- }
- AppendCodepointToUtf8String(codepoint, &str_);
- }
- }
- }
- }
-
- // Accumulate the added string's UTF-16 length.
- ssize_t len = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str_.data()) + new_data_index,
- str_.size() - new_data_index);
- if (len < 0) {
- error_ = "invalid unicode code point";
- return *this;
- }
- utf16_len_ += len;
- return *this;
-}
-
std::u16string Utf8ToUtf16(const StringPiece& utf8) {
ssize_t utf16_length = utf8_to_utf16_length(
reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 7c949b90..0eb35d1 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -59,7 +59,15 @@
// Returns true if the string ends with suffix.
bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
-// Creates a new StringPiece16 that points to a substring of the original string without leading or
+// Creates a new StringPiece that points to a substring of the original string without leading
+// whitespace.
+android::StringPiece TrimLeadingWhitespace(const android::StringPiece& str);
+
+// Creates a new StringPiece that points to a substring of the original string without trailing
+// whitespace.
+android::StringPiece TrimTrailingWhitespace(const android::StringPiece& str);
+
+// Creates a new StringPiece that points to a substring of the original string without leading or
// trailing whitespace.
android::StringPiece TrimWhitespace(const android::StringPiece& str);
@@ -141,9 +149,12 @@
// break the string interpolation.
bool VerifyJavaStringFormat(const android::StringPiece& str);
+bool AppendStyledString(const android::StringPiece& input, bool preserve_spaces,
+ std::string* out_str, std::string* out_error);
+
class StringBuilder {
public:
- explicit StringBuilder(bool preserve_spaces = false);
+ StringBuilder() = default;
StringBuilder& Append(const android::StringPiece& str);
const std::string& ToString() const;
@@ -158,7 +169,6 @@
explicit operator bool() const;
private:
- bool preserve_spaces_;
std::string str_;
size_t utf16_len_ = 0;
bool quote_ = false;
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 2d1242a..d4e3bec 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -41,45 +41,6 @@
EXPECT_TRUE(util::StartsWith("hello.xml", "he"));
}
-TEST(UtilTest, StringBuilderSplitEscapeSequence) {
- EXPECT_THAT(util::StringBuilder().Append("this is a new\\").Append("nline.").ToString(),
- Eq("this is a new\nline."));
-}
-
-TEST(UtilTest, StringBuilderWhitespaceRemoval) {
- EXPECT_THAT(util::StringBuilder().Append(" hey guys ").Append(" this is so cool ").ToString(),
- Eq("hey guys this is so cool"));
- EXPECT_THAT(
- util::StringBuilder().Append(" \" wow, so many \t ").Append("spaces. \"what? ").ToString(),
- Eq(" wow, so many \t spaces. what?"));
- EXPECT_THAT(util::StringBuilder().Append(" where \t ").Append(" \nis the pie?").ToString(),
- Eq("where is the pie?"));
-}
-
-TEST(UtilTest, StringBuilderEscaping) {
- EXPECT_THAT(util::StringBuilder()
- .Append(" hey guys\\n ")
- .Append(" this \\t is so\\\\ cool ")
- .ToString(),
- Eq("hey guys\n this \t is so\\ cool"));
- EXPECT_THAT(util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString(), Eq("@?#\\\'"));
-}
-
-TEST(UtilTest, StringBuilderMisplacedQuote) {
- util::StringBuilder builder;
- EXPECT_FALSE(builder.Append("they're coming!"));
-}
-
-TEST(UtilTest, StringBuilderUnicodeCodes) {
- EXPECT_THAT(util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString(),
- Eq("\u00AF\u0AF0 woah"));
- EXPECT_FALSE(util::StringBuilder().Append("\\u00 yo"));
-}
-
-TEST(UtilTest, StringBuilderPreserveSpaces) {
- EXPECT_THAT(util::StringBuilder(true /*preserve_spaces*/).Append("\"").ToString(), Eq("\""));
-}
-
TEST(UtilTest, TokenizeInput) {
auto tokenizer = util::Tokenize(StringPiece("this| is|the|end"), '|');
auto iter = tokenizer.begin();
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index e7b269a..9183918 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -411,6 +411,11 @@
case SECTION_LOG:
printf(" new LogSection(%d, %s),\n", field->number(), s.args().c_str());
break;
+ case SECTION_GZIP:
+ printf(" new GZipSection(%d,", field->number());
+ splitAndPrint(s.args());
+ printf(" NULL),\n");
+ break;
}
}
printf(" NULL };\n");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 06a5c2e..8529a89 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -27,6 +27,7 @@
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -420,9 +421,12 @@
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (peerHandle == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer handle - cannot be null");
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+ Build.VERSION_CODES.P)) {
+ if (peerHandle == null) {
+ throw new IllegalArgumentException(
+ "createNetworkSpecifier: Invalid peer handle - cannot be null");
+ }
}
return new WifiAwareNetworkSpecifier(
@@ -453,9 +457,12 @@
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (peer == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer MAC - cannot be null");
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+ Build.VERSION_CODES.P)) {
+ if (peer == null) {
+ throw new IllegalArgumentException(
+ "createNetworkSpecifier: Invalid peer MAC - cannot be null");
+ }
}
if (peer != null && peer.length != 6) {
throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
index fda7a9a..3ece93d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
@@ -16,6 +16,8 @@
package android.net.wifi.aware;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.Constants;
/**
@@ -84,4 +86,21 @@
return true;
}
+
+ /**
+ * Returns true if the App version is older than minVersion.
+ */
+ public static boolean isLegacyVersion(Context context, int minVersion) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(context.getOpPackageName(), 0)
+ .targetSdkVersion < minVersion) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify valididity before checking App's version.
+ }
+ return false;
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 84e3ed9..272f727 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,14 +19,20 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.wifi.RttManager;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
@@ -79,12 +85,32 @@
@Mock
public RttManager.RttListener mockRttListener;
+ @Mock
+ public PackageManager mockPackageManager;
+
+ @Mock
+ public ApplicationInfo mockApplicationInfo;
+
private static final int AWARE_STATUS_ERROR = -1;
+ private static final byte[] PMK_VALID = "01234567890123456789012345678901".getBytes();
+ private static final byte[] PMK_INVALID = "012".getBytes();
+
+ private static final String PASSPHRASE_VALID = "SomeLongEnoughPassphrase";
+ private static final String PASSPHRASE_TOO_SHORT = "012";
+ private static final String PASSPHRASE_TOO_LONG =
+ "0123456789012345678901234567890123456789012345678901234567890123456789";
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+ when(mockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ mockApplicationInfo);
+ when(mockContext.getOpPackageName()).thenReturn("XXX");
+ when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
+
mDut = new WifiAwareManager(mockContext, mockAwareService);
mMockLooper = new TestLooper();
mMockLooperHandler = new Handler(mMockLooper.getLooper());
@@ -884,8 +910,8 @@
final int sessionId = 123;
final PeerHandle peerHandle = new PeerHandle(123412);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
- final byte[] pmk = "01234567890123456789012345678901".getBytes();
- final String passphrase = "A really bad password";
+ final byte[] pmk = PMK_VALID;
+ final String passphrase = PASSPHRASE_VALID;
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -965,8 +991,8 @@
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
- final byte[] pmk = "01234567890123456789012345678901".getBytes();
- final String passphrase = "A really bad password";
+ final byte[] pmk = PMK_VALID;
+ final String passphrase = PASSPHRASE_VALID;
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
@@ -1030,7 +1056,7 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierWithClientIncorrectLengthPmk() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), true, "012".getBytes(), null);
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), true, PMK_INVALID, null);
}
/**
@@ -1045,17 +1071,17 @@
* Validate that a too short Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierWithClientShortPassphrase() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, "012");
+ public void testNetworkSpecifierWithClientTooShortPassphrase() throws Exception {
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
+ PASSPHRASE_TOO_SHORT);
}
/**
* Validate that a too long Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierWithClientLongPassphrase() throws Exception {
- executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ public void testNetworkSpecifierWithClientTooLongPassphrase() throws Exception {
+ executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, PASSPHRASE_TOO_LONG);
}
/**
@@ -1063,8 +1089,16 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierWithClientNullPeer() throws Exception {
- executeNetworkSpecifierWithClient(null, false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
+ }
+
+ /**
+ * Validate that a null PeerHandle does not trigger an exception for legacy API.
+ */
+ @Test
+ public void testNetworkSpecifierWithClientNullPeerLegacyApi() throws Exception {
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+ executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
}
private void executeNetworkSpecifierWithClient(PeerHandle peerHandle, boolean doPmk, byte[] pmk,
@@ -1117,7 +1151,7 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectNullPmk() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
- null, null);
+ null, null, true);
}
/**
@@ -1126,7 +1160,7 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectIncorrectLengthPmk() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
- "012".getBytes(), null);
+ PMK_INVALID, null, true);
}
/**
@@ -1135,40 +1169,57 @@
@Test(expected = IllegalArgumentException.class)
public void testNetworkSpecifierDirectNullPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null, null);
+ false, null, null, true);
}
/**
* Validate that a too short Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectShortPassphrase() throws Exception {
+ public void testNetworkSpecifierDirectTooShortPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null, "012");
+ false, null, PASSPHRASE_TOO_SHORT, true);
}
/**
* Validate that a too long Passphrase triggers an exception.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectLongPassphrase() throws Exception {
+ public void testNetworkSpecifierDirectTooLongPassphrase() throws Exception {
executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
- false, null,
- "0123456789012345678901234567890123456789012345678901234567890123456789");
+ false, null, PASSPHRASE_TOO_LONG, true);
}
/**
- * Validate that a null peer MAC triggers an exception.
+ * Validate that a null peer MAC triggers an exception for an Initiator.
*/
@Test(expected = IllegalArgumentException.class)
- public void testNetworkSpecifierDirectNullPeer() throws Exception {
- executeNetworkSpecifierDirect(null, false, null, null);
+ public void testNetworkSpecifierDirectNullPeerInitiator() throws Exception {
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, true);
+ }
+
+ /**
+ * Validate that a null peer MAC triggers an exception for a Resonder.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testNetworkSpecifierDirectNullPeerResponder() throws Exception {
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
+ }
+
+ /**
+ * Validate that a null peer MAC does not trigger an exception for a Resonder on legacy API.
+ */
+ @Test
+ public void testNetworkSpecifierDirectNullPeerResponderLegacyApi() throws Exception {
+ mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+ executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
}
private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk,
- String passphrase) throws Exception {
+ String passphrase, boolean doInitiator) throws Exception {
final int clientId = 134;
- final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+ final int role = doInitiator ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(